First, it changes all classes to explicitly inherit from object. In Python 3, this does nothing, but in Python 2, this changes these classes from old-style classes to new-style classes. This changes some edge cases in python's data model such as removing __getslice__ in favor of calling __getitem__ with a slice object as the index parameter. This improves 2/3 compatibility and just generally makes things better.
The other change is to the cartridge module. This changes the selection of Cartridge classes to be done through inheritance and metaclass programming instead of creating a function that maually selects the appropriate class of cartridge.
Before this change, cartridges were selected by this function:
def load_rom_from_file(path):
with open(path, "rb") as f:
romstring = f.read()
cart_type = romstring[0x147]
if cart_type in MBC1Cartridge.ids:
return MBC1Cartridge(romstring)
else:
raise ValueError("Cartridge of unsupported type.")
Note how the function manually checks the ids in MBC1Cartridge. This means that as the rest of the cartridge types are developed, each would have to be added to this function manually.
This function is changed to this:
def load_rom_from_file(path):
with open(path, "rb") as f:
romstring = f.read()
return Cartridge(romstring)
Now all the work of determining the correct type of cartridge to construct has been moved to within the Cartridge class. load_rom_from_file doesn't need to know anything about the contents of the rom.
The way this works is in to parts. First is the metaclass CartridgeMeta. This metaclass implements a registry so that any object hierarchy which uses it as its metaclass will automatically have a class-level variable called _cartridge_registry added to it, and any subclasses of that base class which define the class-level variable ids will automatically be added to the registry. The metaclass also prevents accidents where the programmer specifies the same cartridge type ID twice, by raising an error if a class attempts to register an id that has already been registered.
Then the __new__ method of Cartridge is defined to check in its internal registry, provided by the metaclass, whenever an instance of Cartridge is requested. Instead of returning a base Cartridge object, the subclass which matches the id in the romstring is returned, and if none match, an error is raised. If no romstring is passed, a DummyCartridge is returned.
Additionally, some unit tests are provided for the Cartridge metaclass/slection system.
This has been tested in both Python 2 and 3.
Note that this request creates a dependency on the "six" module from pypi. This module proves a number of helpers for Python 2/3 compatibility.
This set of changes does two things.
First, it changes all classes to explicitly inherit from
object
. In Python 3, this does nothing, but in Python 2, this changes these classes from old-style classes to new-style classes. This changes some edge cases in python's data model such as removing__getslice__
in favor of calling__getitem__
with aslice
object as the index parameter. This improves 2/3 compatibility and just generally makes things better.The other change is to the cartridge module. This changes the selection of Cartridge classes to be done through inheritance and metaclass programming instead of creating a function that maually selects the appropriate class of cartridge.
Before this change, cartridges were selected by this function:
Note how the function manually checks the
ids
inMBC1Cartridge
. This means that as the rest of the cartridge types are developed, each would have to be added to this function manually.This function is changed to this:
Now all the work of determining the correct type of cartridge to construct has been moved to within the Cartridge class.
load_rom_from_file
doesn't need to know anything about the contents of the rom.The way this works is in to parts. First is the metaclass
CartridgeMeta
. This metaclass implements a registry so that any object hierarchy which uses it as its metaclass will automatically have a class-level variable called_cartridge_registry
added to it, and any subclasses of that base class which define the class-level variableids
will automatically be added to the registry. The metaclass also prevents accidents where the programmer specifies the same cartridge type ID twice, by raising an error if a class attempts to register an id that has already been registered.Then the
__new__
method ofCartridge
is defined to check in its internal registry, provided by the metaclass, whenever an instance ofCartridge
is requested. Instead of returning a baseCartridge
object, the subclass which matches the id in the romstring is returned, and if none match, an error is raised. If no romstring is passed, aDummyCartridge
is returned.Additionally, some unit tests are provided for the
Cartridge
metaclass/slection system.This has been tested in both Python 2 and 3.
Note that this request creates a dependency on the "six" module from pypi. This module proves a number of helpers for Python 2/3 compatibility.