diff --git a/doc/developer.catalog.rst b/doc/developer.catalog.rst index a967369ba..c68ee2527 100644 --- a/doc/developer.catalog.rst +++ b/doc/developer.catalog.rst @@ -8,18 +8,6 @@ To do so, you will need to have the `gambit` GitHub repo cloned and be able to s you may wish to first review the :ref:`contributor guidelines `. You'll also need to have a developer install of `pygambit` available in your Python environment, see :ref:`build-python`. -The catalog module ------------------- - -Although the ``catalog`` directory is located at the project root outside of ``src/pygambit/``, it is installed and bundled as the ``pygambit.catalog`` subpackage. - -This is handled by the package build configuration in ``pyproject.toml`` under ``[tool.setuptools]``: - -- The ``package-dir`` mapping instructs ``setuptools`` to source the ``pygambit.catalog`` subpackage from the physical ``catalog`` directory. -- The ``package-data`` configuration ensures all non-Python data files (like ``.efg`` and ``.nfg`` files) inside the catalog are correctly bundled during installation. - -As a developer, this means you will need to reinstall the package (e.g., passing ``pip install .``) for any new game files or internal catalog changes to be reflected in the ``pygambit`` module. - Add new game files ------------------ @@ -58,3 +46,20 @@ Currently supported representations are: .. warning:: Make sure you commit all changed files e.g. run ``git add --all`` before committing and pushing. + + +Access from pygambit +-------------------- + +We keep the ``catalog`` directory at the top level of the repository because it is in principle independent +of the Python and C++ code. However, in order to include these games with the Python package, there is a bit +of extra infrastructure. + +In ``setup.py`` we have a custom build command which first copies the contents of ``catalog/`` into the build +directory for the Python package. These are then exposed as data in the ``catalog_data`` directory (changing +the name to avoid confusion or clashes with ``catalog.py``, which is responsible for accessing the catalog). + +The main implication is that if you are working via the Python package and you add new games to the catalog, +you will need to rebuild and reinstall the Python extension in order to access the new games. That is, changing +the contents of the catalog is no different than changing any other source code in the Python package; you'll +need to execute ``pip install .`` after the addition or change. diff --git a/pyproject.toml b/pyproject.toml index 71134753d..553e949fd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,11 +114,11 @@ markers = [ ] [tool.setuptools] -packages = ["pygambit", "pygambit.catalog"] -package-dir = { "pygambit" = "src/pygambit", "pygambit.catalog" = "catalog" } +packages = ["pygambit"] +package-dir = { "pygambit" = "src/pygambit" } [tool.setuptools.package-data] -"pygambit.catalog" = ["*"] +pygambit = ["catalog_data/**/*"] [tool.setuptools.dynamic] version = {file = "build_support/GAMBIT_VERSION"} diff --git a/setup.py b/setup.py index 4a1e82b5a..9b47eb533 100644 --- a/setup.py +++ b/setup.py @@ -21,10 +21,12 @@ # import glob +import pathlib import platform +import shutil import Cython.Build -import setuptools +import setuptools.command.build_py cppgambit_include_dirs = ["src"] cppgambit_cflags = ( @@ -78,6 +80,19 @@ def solver_library_config(library_name: str, paths: list) -> tuple: ) +class GambitBuildPy(setuptools.command.build_py.build_py): + """Extend `build_py` to copy the catalog games data into the build library.""" + def run(self) -> None: + super().run() + + catalog_source = pathlib.Path("catalog") + catalog_target = pathlib.Path(self.build_lib) / "pygambit/catalog_data" + if catalog_target.exists(): + shutil.rmtree(catalog_target) + catalog_target.mkdir(exist_ok=True, parents=True) + shutil.copytree(catalog_source, catalog_target, dirs_exist_ok=True) + + cppgambit_bimatrix = solver_library_config("cppgambit_bimatrix", ["linalg", "lp", "lcp", "enummixed"]) cppgambit_liap = solver_library_config("cppgambit_liap", ["liap"]) @@ -100,6 +115,7 @@ def solver_library_config(library_name: str, paths: list) -> tuple: ) setuptools.setup( + cmdclass={"build_py": GambitBuildPy}, libraries=[cppgambit_bimatrix, cppgambit_liap, cppgambit_logit, cppgambit_simpdiv, cppgambit_gtracer, cppgambit_enumpoly, cppgambit_games, cppgambit_core], diff --git a/catalog/__init__.py b/src/pygambit/catalog.py similarity index 97% rename from catalog/__init__.py rename to src/pygambit/catalog.py index 26104acdd..6b24f17eb 100644 --- a/catalog/__init__.py +++ b/src/pygambit/catalog.py @@ -6,8 +6,8 @@ import pygambit as gbt -# Use the full string path to the virtual package we created -_CATALOG_RESOURCE = files(__name__) +# Use the full string path to where the catalog data are placed in the package +_CATALOG_RESOURCE = files("pygambit")/"catalog_data" READERS = { ".nfg": gbt.read_nfg,