Portál AbcLinuxu, 27. června 2025 12:40


Dotaz: OpenSky Sudoku Generator - převod z Python2.4 do Python3.+

15.8.2024 08:27 Host
OpenSky Sudoku Generator - převod z Python2.4 do Python3.+
Přečteno: 567×
Odpovědět | Admin
Příloha:
Zdravím, chtěl bych si převést starší projekt v Python2 do novějšího. Původní kód je zde.

Při převodu jsem se zarazil na funkcích z modulu pickle, autor použil verzi z Python2.4, která je závislá (asi) na modulu Numeric, novější Python ji nahrazuje za numpy.

Pomocí ChatGPT se mi povedla konverze do Python3, ale nefunguje správně načítání (serializace/deserializace) uloženého objektu pickle.load().

Problém je, že výstupem f-ce loadPuzzles(..) je typ None, nenačtou se správně uložená data, formát je asi odlišný z důvodu starší verze pickle (Python2.4). V přiloženém souboru je ukázkový typ dat.

Řešením je asi převod uložených dat v jiném formátu přes pickle.load(f)/json.dump(data, f), ale narazil jsem na problém dostupnosti linux distribucí s funkčním Python2.4

Mohl by někdo pomoci?

Chybová zpráva:
    return super().find_class(module, name)
AttributeError: Can't get attribute 'array_constructor' on module 'numpy' from '/usr/lib64/python3.10/site-packages/numpy/__init__.py'
Error loading data: 'NoneType' object is not callable
Error loading puzzle from lib/games/Medium/tmprPirlZ: Unpickled data is None.
AttributeError: Can't get attribute 'array_constructor' on module 'numpy' from '/usr/lib64/python3.10/site-packages/numpy/__init__.py' for module: numpy, name: array_constructor
Originální kód:
class MyUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        # help unpickle find the correct module (since sys.path is different 
        # from when we generated the puzzles)
        if module == 'sudoku':
            return getattr(sudoku, name)
        return pickle.Unpickler.find_class(self, module, name)

def loadPuzzles(num, difficulty='Any'):
    indexfile = os.path.join(DATA_DIR, difficulty + ".index")
    index = file(indexfile).readlines()
    puzzlepaths = random.sample(index, num)
    puzzles = []
    g = sudoku_maker.SudokuGenerator()
    for path in puzzlepaths:
        path = path.strip()
        infile = os.path.join(DATA_DIR, path)
        puz = MyUnpickler(file(infile)).load()
        d = g.assess_difficulty(puz.grid)
        puzzles.append((puz, d))
    return puzzles
Převedený kód:
class MyUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module == 'Numeric':
            # Redirect to numpy
            module = 'numpy'
        elif module == 'sudoku':
            return getattr(sudoku, name)
        elif module == 'numpy':
            if name == 'array_constructor':
                # Handle the specific case for array_constructor
                # You can return np.array or a custom function if needed
                return np.array  # or whatever function you need to return
        # Add a fallback for unknown classes
        try:
            return super().find_class(module, name)
        except AttributeError as e:
            print(f"AttributeError: {e} for module: {module}, name: {name}")
            # Optionally log the entire traceback
            import traceback
            traceback.print_exc()

def load_my_data(file):
    # Check if the input is a string (file path) or a file object
    if isinstance(file, str):
        with open(file, 'rb') as f:
            return MyUnpickler(f).load()
            #return pickle.Unpickler(f).load()
    else:
         try:
            return MyUnpickler(file).load()
         except Exception as e:
            print(f"Error loading data: {e}")
            return None

def loadPuzzles(num, difficulty='Any'):
    indexfile = os.path.join(DATA_DIR, difficulty + ".index")

    with open(indexfile, 'r') as file:
        index = file.read().strip().splitlines()  # Read lines into a list
    
    puzzlepaths = random.sample(index, num)
    puzzles = []
    g = sudoku_maker.SudokuGenerator()
    
    for path in puzzlepaths:
        path = path.strip()
        infile = os.path.join(DATA_DIR, path)
        
        try:
            with open(infile, 'rb') as f:  # Open the file in binary mode
                puz = load_my_data(f)  # Use the file object here
                
                if puz is None:
                    raise ValueError("Unpickled data is None.")
                
                d = g.assess_difficulty(puz.grid)
                puzzles.append((puz, d))
        
        except Exception as e:
            print(f"Error loading puzzle from {infile}: {e}")
            # Continue to the next puzzle instead of returning None

    return puzzles  # Return the list of puzzles, which may be empty if none were loaded

Nástroje: Začni sledovat (0) ?Zašle upozornění na váš email při vložení nového komentáře.

Odpovědi

15.8.2024 21:16 X
Rozbalit Rozbalit vše Re: OpenSky Sudoku Generator - převod z Python2.4 do Python3.+
Odpovědět | | Sbalit | Link | Blokovat | Admin
Dve veci. Kdyz rozeberes ten pickle soubor: import pickletools

with open("tmpzVugvu",'rb') as fh: pickletools.dis(fh)
je tam skutence Numeric array:
5036: s    SETITEM
 5037: S    STRING     'grid'
 5045: p    PUT        362
 5050: c    GLOBAL     'Numeric array_constructor'
 5077: p    PUT        363
 5082: (    MARK
 5083: (        MARK
 5084: I            INT        9
 5087: I            INT        9
 5090: t            TUPLE      (MARK at 5083)
 5091: p        PUT        364
 5096: S        STRING     'b'
 5101: p        PUT        365
 5106: S        STRING     '\x02\x00\x00\x00\x00\x07\x01\x00\x00\x00\x08\x00\x00\x00\x06\x05\x03\t\x00\x00\x00\x00\t\x04\x02\x08\x00\x00\x00\x00\x00\x04\x00\x00\x02\x00\x01\x00\x00\x00\x00\x00\x00\x00\x05\x00\x07\x00\x00\x08\x00\x00\x00\x00\x00\x01\t\x03\x05\x00\x00\x00\x00\x03\x05\x07\x04\x00\x00\x00\t\x00\x00\x00\x06\x07\x00\x00\x00\x00\x04'
 5426: p        PUT        366
Podle retezce 'grid' zjistis, ze v sudoku.py je trida SudokuGrid, ktera vyrabi self.grid = Numeric.array(self.grid,typecode='b').

Zaroven je to jedine misto v puvodnim kodu, kde se modul Numeric pouziva.

Zadruhe, ChatGPT je k h*vnu, protoze tvoje podminka: if module == 'Numeric': # Redirect to numpy module = 'numpy' elif module == 'sudoku': return getattr(sudoku, name) elif module == 'numpy': if name == 'array_constructor': # Handle the specific case for array_constructor # You can return np.array or a custom function if needed return np.array # or whatever function you need to return # Add a fallback for unknown classes nedava smysl.
16.8.2024 11:15 Host
Rozbalit Rozbalit vše Re: OpenSky Sudoku Generator - převod z Python2.4 do Python3.+
Díky za odpověď. Už se v tom začínám více orientovat. Výstupem má být objekt SudokuGrid, 9x9, s načtenými hodnotami z toho souboru tmp... .

Původní konstrukce Numeric očekává 4 parametry - array_constructor((9,9), 'b','\x00\x07\x00\x00\x00\x08\x...', True),

NumPy array má jiné parametry. Zkusím nahradit array_constructor za NumPy array, ale tady nevím jak vytvořit pole 9x9 s předvolenými hodnotami z toho souboru.
16.8.2024 12:47 X
Rozbalit Rozbalit vše Re: OpenSky Sudoku Generator - převod z Python2.4 do Python3.+
Jak pise kolega dole, snazit se zachranovat soubory nahodne vygenerovanych her nema moc vyznam. Prepis to tak, aby generovani her pouzivalo np => prepis tridu "SudokuGrid" a vygeneruj si novych her kolik chces. Tim se zbavis celeho problemu a zaroven tim vyresis i nacitani.
16.8.2024 13:36 Host
Rozbalit Rozbalit vše Re: OpenSky Sudoku Generator - převod z Python2.4 do Python3.+
No to je právě ten problém, generate.py vola funkci printpuzzles.go(tfile, num, difficulty, solutions, footer, perPage)

Uvnitř je loadPuzzles(..., s problematickou vazbou na Numeric.

Ale už jsem pokročil dále, vytvořil jsem soubor Numeric.py a už se mi načítá uložený grid, bohužel už se nějak nenaplní správné řešení. Tak jdu ještě pátrat dále.

Současné chybky:
['Hard/tmppJClrx']
Numeric array_constructor:
[[2 9 0 0 0 0 0 0 6]
 [0 8 0 0 2 6 0 3 0]
 [0 3 0 5 0 0 2 0 0]
 [5 0 3 0 4 0 0 2 0]
 [1 0 0 0 7 0 0 0 5]
 [0 7 0 0 8 0 4 0 3]
 [0 0 9 0 0 8 0 7 0]
 [0 5 0 4 6 0 0 9 0]
 [8 0 0 0 0 0 0 5 4]]
Impossible!
Puzzle was:
Solution: 
Grid
       4 9 8 6 7 2 3 1 5
       2 6 3 4 5 1 9 8 7
       7 1 5 3 8 9 2 4 6
       3 5 7 8 2 6 1 9 4
       8 4 1 9 3 7 6 5 2
       6 2 9 1 4 5 7 3 8
       5 8 6 2 9 3 4 7 1
       9 7 2 5 1 4 8 6 3
       1 3 4 7 6 8 5 2 9
Puzzle foobared in following state:
Error loading puzzle from lib/games/Hard/tmppJClrx: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Numeric.py
import numpy as np

def array_constructor(pole, typecode, hex_string, typ=True):

    # Převod hexadecimálního řetězce na bajty
    byte_array = bytes(hex_string, 'latin1')

    # Vytvoření NumPy pole z bajtového pole
    numpy_array = np.frombuffer(byte_array, dtype=np.uint8)

    # Zkontrolujte, zda má pole dostatečný počet prvků pro 9x9
    if numpy_array.size < 81:
        raise ValueError("Hex string does not contain enough data for a 9x9 array.")

    # Přetvoření pole na rozměry 9x9
    numpy_array_reshaped = numpy_array[:81].reshape(9, 9)
    print ("Numeric array_constructor:")
    print (numpy_array_reshaped)
    return numpy_array_reshaped
16.8.2024 15:06 Host
Rozbalit Rozbalit vše Re: OpenSky Sudoku Generator - převod z Python2.4 do Python3.+
Opravil jsem několik řádků v sudoku.py a už to začíná dávat nějaké výsledky, jestli jsou správné uvidíme.

Zatím tedy děkuji za spolupráci :)

class SudokuGrid:
        '''
        if grid:
            if type(grid)==str:
        '''
        if grid is not False:  # Změna podmínky
            if isinstance(grid, str):
class DifficultyRating:
    def count_values(self, dct):
        kk = list(dct.keys())  # Převod dict_keys na seznam, puvodne kk=dct.keys()
        kk.sort()  # Nyní můžete použít sort()
        return [len(dct[k]) for k in kk]

16.8.2024 00:09 .
Rozbalit Rozbalit vše Re: OpenSky Sudoku Generator - převod z Python2.4 do Python3.+
Odpovědět | | Sbalit | Link | Blokovat | Admin
Ten web je zrovna nefunkční, tak se na to nekouknu, ale asi ty pickly nemusíš konvertovat a prostě vygeneruj nové skriptem gnerate.py a pak zindexuj skriptem makeindices.sh.

Založit nové vláknoNahoru

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.