diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 5d7efc700..8b7133b74 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -31,8 +31,21 @@ jobs: libbz2-dev \ ninja-build \ software-properties-common + - name: Install Python 3.12 with the latest version of uv. + uses: astral-sh/setup-uv@v7 + with: + python-version: '3.12' - uses: actions/checkout@v4 + - name: Create venv and install build dependencies + run: | + uv venv --isolated + source .venv/bin/activate + uv pip install scikit-build-core~=0.10 numpy pybind11-stubgen pybind11~=2.2 setuptools-scm~=9.2.2 - name: Check CMake configure - run: cmake -B build -G Ninja + run: | + source .venv/bin/activate + cmake -B build -G Ninja - name: CMake build - run: cmake --build build + run: | + source .venv/bin/activate + cmake --build build diff --git a/CMakeLists.txt b/CMakeLists.txt index 48a45e4b8..cbc4a3cb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,8 @@ message(STATUS "HACKDIR set to: ${HACKDIR}") set(VARDIR ${HACKDIR}) set(INSTDIR ${HACKDIR}) +# Use FindPython in PyBind11 to find Python. +set(PYBIND11_FINDPYTHON NEW) # pybind11 via FetchContent include(FetchContent) FetchContent_Declare( @@ -65,6 +67,7 @@ FetchContent_Declare( GIT_TAG v3.0.1 EXCLUDE_FROM_ALL) FetchContent_MakeAvailable(pybind11) +message(STATUS "Python_EXECUTABLE is set to: ${Python_EXECUTABLE}") # de-boost-ified version of boost.context via FetchContent FetchContent_Declare( @@ -210,10 +213,38 @@ set_target_properties(_pyconverter PROPERTIES CXX_STANDARD 14) target_include_directories( _pyconverter PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/third_party/converter) +set(STUB_DIR ${CMAKE_CURRENT_BINARY_DIR}/stubs) +set_directory_properties(PROPERTIES ADDITIONAL_CLEAN_FILES "${STUB_DIR}") +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/stubs/nle/_pyconverter.pyi + COMMAND ${Python_EXECUTABLE} -m pybind11_stubgen _pyconverter --output-dir + ${STUB_DIR} + WORKING_DIRECTORY $ + DEPENDS _pyconverter + COMMENT "Generating type stubs ..." + VERBATIM) + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/stubs/nle/_pynethack/__init__.pyi + COMMAND ${Python_EXECUTABLE} -m pybind11_stubgen _pynethack --output-dir + ${STUB_DIR} --ignore-unresolved-names game_end_types + WORKING_DIRECTORY $ + DEPENDS _pynethack + COMMENT "Generating type stubs ..." + VERBATIM) + +add_custom_target( + my_module_stubs ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/stubs/nle/_pynethack/__init__.pyi + ${CMAKE_CURRENT_BINARY_DIR}/stubs/nle/_pyconverter.pyi) + +file(TOUCH ${CMAKE_CURRENT_BINARY_DIR}/py.typed) + set(TILE_FILES "win/share/monsters.txt" "win/share/objects.txt" "win/share/other.txt") install(FILES ${TILE_FILES} DESTINATION ${INSTDIR}/tiles) + # Only install if we are building as part of a Python project. if(DEFINED SKBUILD_PROJECT_VERSION) install( @@ -221,4 +252,13 @@ if(DEFINED SKBUILD_PROJECT_VERSION) RUNTIME DESTINATION ${PYTHON_PACKAGE_NAME} LIBRARY DESTINATION ${PYTHON_PACKAGE_NAME} ARCHIVE DESTINATION ${PYTHON_PACKAGE_NAME}) + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/py.typed + DESTINATION ${PYTHON_PACKAGE_NAME}) + + install( + DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/stubs/" + DESTINATION ${PYTHON_PACKAGE_NAME} + FILES_MATCHING + PATTERN "*.pyi") endif() diff --git a/nle/nethack/__init__.py b/nle/nethack/__init__.py index a80008b04..39989c1b1 100644 --- a/nle/nethack/__init__.py +++ b/nle/nethack/__init__.py @@ -1,5 +1,163 @@ # Copyright (c) Facebook, Inc. and its affiliates. -from nle._pynethack.nethack import * # noqa: F403 +import nle._pynethack.nethack +from nle._pynethack.nethack import AMULET_CLASS +from nle._pynethack.nethack import ARMOR_CLASS +from nle._pynethack.nethack import ASCENDED +from nle._pynethack.nethack import BALL_CLASS +from nle._pynethack.nethack import BL_MASK_BITS +from nle._pynethack.nethack import BL_MASK_BLIND +from nle._pynethack.nethack import BL_MASK_CONF +from nle._pynethack.nethack import BL_MASK_DEAF +from nle._pynethack.nethack import BL_MASK_FLY +from nle._pynethack.nethack import BL_MASK_FOODPOIS +from nle._pynethack.nethack import BL_MASK_HALLU +from nle._pynethack.nethack import BL_MASK_LEV +from nle._pynethack.nethack import BL_MASK_RIDE +from nle._pynethack.nethack import BL_MASK_SLIME +from nle._pynethack.nethack import BL_MASK_STONE +from nle._pynethack.nethack import BL_MASK_STRNGL +from nle._pynethack.nethack import BL_MASK_STUN +from nle._pynethack.nethack import BL_MASK_TERMILL +from nle._pynethack.nethack import BURNING +from nle._pynethack.nethack import CHAIN_CLASS +from nle._pynethack.nethack import CHOKING +from nle._pynethack.nethack import COIN_CLASS +from nle._pynethack.nethack import COLNO +from nle._pynethack.nethack import CRUSHING +from nle._pynethack.nethack import DIED +from nle._pynethack.nethack import DISSOLVED +from nle._pynethack.nethack import DROWNING +from nle._pynethack.nethack import ESCAPED +from nle._pynethack.nethack import EXPL_MAX +from nle._pynethack.nethack import FOOD_CLASS +from nle._pynethack.nethack import GEM_CLASS +from nle._pynethack.nethack import GENOCIDED +from nle._pynethack.nethack import GLYPH_BODY_OFF +from nle._pynethack.nethack import GLYPH_CMAP_OFF +from nle._pynethack.nethack import GLYPH_DETECT_OFF +from nle._pynethack.nethack import GLYPH_EXPLODE_OFF +from nle._pynethack.nethack import GLYPH_INVIS_OFF +from nle._pynethack.nethack import GLYPH_INVISIBLE +from nle._pynethack.nethack import GLYPH_MON_OFF +from nle._pynethack.nethack import GLYPH_OBJ_OFF +from nle._pynethack.nethack import GLYPH_PET_OFF +from nle._pynethack.nethack import GLYPH_RIDDEN_OFF +from nle._pynethack.nethack import GLYPH_STATUE_OFF +from nle._pynethack.nethack import GLYPH_SWALLOW_OFF +from nle._pynethack.nethack import GLYPH_WARNING_OFF +from nle._pynethack.nethack import GLYPH_ZAP_OFF +from nle._pynethack.nethack import ILLOBJ_CLASS +from nle._pynethack.nethack import MAX_GLYPH +from nle._pynethack.nethack import MAXEXPCHARS +from nle._pynethack.nethack import MAXMCLASSES +from nle._pynethack.nethack import MAXOCLASSES +from nle._pynethack.nethack import MAXPCHARS +from nle._pynethack.nethack import MAXWIN +from nle._pynethack.nethack import MG_BW_LAVA +from nle._pynethack.nethack import MG_CORPSE +from nle._pynethack.nethack import MG_DETECT +from nle._pynethack.nethack import MG_INVIS +from nle._pynethack.nethack import MG_OBJPILE +from nle._pynethack.nethack import MG_PET +from nle._pynethack.nethack import MG_RIDDEN +from nle._pynethack.nethack import MG_STATUE +from nle._pynethack.nethack import NHW_MAP +from nle._pynethack.nethack import NHW_MENU +from nle._pynethack.nethack import NHW_MESSAGE +from nle._pynethack.nethack import NHW_STATUS +from nle._pynethack.nethack import NHW_TEXT +from nle._pynethack.nethack import NLE_BL_AC +from nle._pynethack.nethack import NLE_BL_ALIGN +from nle._pynethack.nethack import NLE_BL_CAP +from nle._pynethack.nethack import NLE_BL_CHA +from nle._pynethack.nethack import NLE_BL_CON +from nle._pynethack.nethack import NLE_BL_CONDITION +from nle._pynethack.nethack import NLE_BL_DEPTH +from nle._pynethack.nethack import NLE_BL_DEX +from nle._pynethack.nethack import NLE_BL_DLEVEL +from nle._pynethack.nethack import NLE_BL_DNUM +from nle._pynethack.nethack import NLE_BL_ENE +from nle._pynethack.nethack import NLE_BL_ENEMAX +from nle._pynethack.nethack import NLE_BL_EXP +from nle._pynethack.nethack import NLE_BL_GOLD +from nle._pynethack.nethack import NLE_BL_HD +from nle._pynethack.nethack import NLE_BL_HP +from nle._pynethack.nethack import NLE_BL_HPMAX +from nle._pynethack.nethack import NLE_BL_HUNGER +from nle._pynethack.nethack import NLE_BL_INT +from nle._pynethack.nethack import NLE_BL_SCORE +from nle._pynethack.nethack import NLE_BL_STR25 +from nle._pynethack.nethack import NLE_BL_STR125 +from nle._pynethack.nethack import NLE_BL_TIME +from nle._pynethack.nethack import NLE_BL_WIS +from nle._pynethack.nethack import NLE_BL_X +from nle._pynethack.nethack import NLE_BL_XP +from nle._pynethack.nethack import NLE_BL_Y +from nle._pynethack.nethack import NLE_BLSTATS_SIZE +from nle._pynethack.nethack import NLE_INTERNAL_SIZE +from nle._pynethack.nethack import NLE_INVENTORY_SIZE +from nle._pynethack.nethack import NLE_INVENTORY_STR_LENGTH +from nle._pynethack.nethack import NLE_MESSAGE_SIZE +from nle._pynethack.nethack import NLE_MISC_SIZE +from nle._pynethack.nethack import NLE_PROGRAM_STATE_SIZE +from nle._pynethack.nethack import NLE_SCREEN_DESCRIPTION_LENGTH +from nle._pynethack.nethack import NLE_TERM_CO +from nle._pynethack.nethack import NLE_TERM_LI +from nle._pynethack.nethack import NO_GLYPH +from nle._pynethack.nethack import NUM_OBJECTS +from nle._pynethack.nethack import NUM_ZAP +from nle._pynethack.nethack import NUMMONS +from nle._pynethack.nethack import OBJ_DESCR +from nle._pynethack.nethack import OBJ_NAME +from nle._pynethack.nethack import PANICKED +from nle._pynethack.nethack import POISONING +from nle._pynethack.nethack import POTION_CLASS +from nle._pynethack.nethack import QUIT +from nle._pynethack.nethack import RANDOM_CLASS +from nle._pynethack.nethack import RING_CLASS +from nle._pynethack.nethack import ROCK_CLASS +from nle._pynethack.nethack import ROWNO +from nle._pynethack.nethack import SCROLL_CLASS +from nle._pynethack.nethack import SPBOOK_CLASS +from nle._pynethack.nethack import STARVING +from nle._pynethack.nethack import STONING +from nle._pynethack.nethack import TILE_X +from nle._pynethack.nethack import TILE_Y +from nle._pynethack.nethack import TILE_Z +from nle._pynethack.nethack import TOOL_CLASS +from nle._pynethack.nethack import TRICKED +from nle._pynethack.nethack import TURNED_SLIME +from nle._pynethack.nethack import VENOM_CLASS +from nle._pynethack.nethack import WAND_CLASS +from nle._pynethack.nethack import WARNCOUNT +from nle._pynethack.nethack import WEAPON_CLASS +from nle._pynethack.nethack import class_sym +from nle._pynethack.nethack import game_end_types +from nle._pynethack.nethack import glyph2tile +from nle._pynethack.nethack import glyph_is_body +from nle._pynethack.nethack import glyph_is_cmap +from nle._pynethack.nethack import glyph_is_detected_monster +from nle._pynethack.nethack import glyph_is_invisible +from nle._pynethack.nethack import glyph_is_monster +from nle._pynethack.nethack import glyph_is_normal_monster +from nle._pynethack.nethack import glyph_is_normal_object +from nle._pynethack.nethack import glyph_is_object +from nle._pynethack.nethack import glyph_is_pet +from nle._pynethack.nethack import glyph_is_ridden_monster +from nle._pynethack.nethack import glyph_is_statue +from nle._pynethack.nethack import glyph_is_swallow +from nle._pynethack.nethack import glyph_is_trap +from nle._pynethack.nethack import glyph_is_warning +from nle._pynethack.nethack import glyph_to_cmap +from nle._pynethack.nethack import glyph_to_mon +from nle._pynethack.nethack import glyph_to_obj +from nle._pynethack.nethack import glyph_to_swallow +from nle._pynethack.nethack import glyph_to_trap +from nle._pynethack.nethack import glyph_to_warning +from nle._pynethack.nethack import objclass +from nle._pynethack.nethack import objdescr +from nle._pynethack.nethack import permonst +from nle._pynethack.nethack import symdef from nle.nethack.actions import * # noqa: F403 from nle.nethack.nethack import BLSTATS_SHAPE from nle.nethack.nethack import DUNGEON_SHAPE diff --git a/nle/tests/test_dataset.py b/nle/tests/test_dataset.py index fd66015f2..c91151999 100644 --- a/nle/tests/test_dataset.py +++ b/nle/tests/test_dataset.py @@ -4,16 +4,16 @@ import numpy as np import pytest -from test_converter import COLSROWS -from test_converter import FINALFRAME -from test_converter import FINALFRAMECOLORS -from test_converter import TIMESTAMPS -from test_converter import getfilename -from test_db import conn # noqa: F401 -from test_db import mockdata # noqa: F401 from nle.dataset import dataset from nle.dataset import db +from nle.tests.test_converter import COLSROWS +from nle.tests.test_converter import FINALFRAME +from nle.tests.test_converter import FINALFRAMECOLORS +from nle.tests.test_converter import TIMESTAMPS +from nle.tests.test_converter import getfilename +from nle.tests.test_db import conn # noqa: F401 +from nle.tests.test_db import mockdata # noqa: F401 class TestDataset: diff --git a/nle/tests/test_db.py b/nle/tests/test_db.py index 770cada33..69fd4baee 100644 --- a/nle/tests/test_db.py +++ b/nle/tests/test_db.py @@ -1,9 +1,10 @@ import os import pytest -import test_converter import nle.env.tasks +import nle.nethack +import nle.tests.test_converter as test_converter from nle.dataset import db from nle.dataset import populate_db diff --git a/nle/tests/test_populate_db.py b/nle/tests/test_populate_db.py index 3ee5438fe..d7cebaf45 100644 --- a/nle/tests/test_populate_db.py +++ b/nle/tests/test_populate_db.py @@ -1,11 +1,11 @@ import json import pytest # NOQA: F401 -from test_converter import getfilename -from test_db import conn # NOQA: F401 -from test_db import mockdata # NOQA: F401 from nle import nethack +from nle.tests.test_converter import getfilename +from nle.tests.test_db import conn # NOQA: F401 +from nle.tests.test_db import mockdata # NOQA: F401 TTYRECS_TABLE_OFFSET = 0 GAMES_TABLE_OFFSET = 5 diff --git a/nle/tests/test_tiles.py b/nle/tests/test_tiles.py index 42d866c8a..f80a4b3be 100644 --- a/nle/tests/test_tiles.py +++ b/nle/tests/test_tiles.py @@ -2,7 +2,7 @@ import numpy as np import pytest -import nle +import nle.nethack class TestTileset: diff --git a/pyproject.toml b/pyproject.toml index d1ff3031e..520cd9da6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,9 +28,16 @@ file = "LICENSE" [project.urls] Homepage = "https://github.com/NetHack-LE/nle" + [build-system] # Lock build-deps as uv does not yet support locking of build deps: astral-sh/uv#5190 -requires = ["scikit-build-core~=0.10", "pybind11~=2.2", "setuptools-scm~=9.2.2"] +requires = [ + "scikit-build-core~=0.10", + "numpy", + "pybind11-stubgen", + "pybind11~=2.2", + "setuptools-scm~=9.2.2", +] build-backend = "scikit_build_core.build" [tool.scikit-build] @@ -42,6 +49,9 @@ generate = [ { path = "nle/version.py", template = '__version__ = "${version}"' }, ] +#[tool.uv] +#managed = false + [tool.setuptools_scm] # This section is necessary to activate setuptools-scm, but can be empty @@ -53,7 +63,7 @@ nle-ttyplay2 = "nle.scripts.ttyplay2:main" nle-read-tty = "nle.scripts.read_tty:main" [dependency-groups] -dev = ["nle[dev]"] +dev = ["nle[dev]", "pyrefly>=0.57.1"] [project.optional-dependencies] agent = ["torch>=1.3.1"] @@ -115,3 +125,8 @@ before-all = "rm -rf {project}/build {project}/*.so {project}/CMakeCache.txt && [tool.pytest.ini_options] testpaths = ["nle/tests"] + +[tool.pyrefly] +disable-search-path-heuristics = true +project-includes = ["."] +project-excludes = ["src/**"] diff --git a/uv.lock b/uv.lock index 170285647..f9470b464 100644 --- a/uv.lock +++ b/uv.lock @@ -485,6 +485,7 @@ dev = [ [package.dev-dependencies] dev = [ { name = "nle", extra = ["dev"] }, + { name = "pyrefly" }, ] [package.metadata] @@ -507,7 +508,10 @@ requires-dist = [ provides-extras = ["agent", "dev", "all"] [package.metadata.requires-dev] -dev = [{ name = "nle", extras = ["dev"] }] +dev = [ + { name = "nle", extras = ["dev"] }, + { name = "pyrefly", specifier = ">=0.57.1" }, +] [[package]] name = "nodeenv" @@ -898,6 +902,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "pyrefly" +version = "0.57.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c1/c17211e5bbd2b90a24447484713da7cc2cee4e9455e57b87016ffc69d426/pyrefly-0.57.1.tar.gz", hash = "sha256:b05f6f5ee3a6a5d502ca19d84cb9ab62d67f05083819964a48c1510f2993efc6", size = 5310800, upload-time = "2026-03-18T18:42:35.614Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/58/8af37856c8d45b365ece635a6728a14b0356b08d1ff1ac601d7120def1e0/pyrefly-0.57.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:91974bfbe951eebf5a7bc959c1f3921f0371c789cad84761511d695e9ab2265f", size = 12681847, upload-time = "2026-03-18T18:42:10.963Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d7/fae6dd9d0355fc5b8df7793f1423b7433ca8e10b698ea934c35f0e4e6522/pyrefly-0.57.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:808087298537c70f5e7cdccb5bbaad482e7e056e947c0adf00fb612cbace9fdc", size = 12219634, upload-time = "2026-03-18T18:42:13.469Z" }, + { url = "https://files.pythonhosted.org/packages/29/8f/9511ae460f0690e837b9ba0f7e5e192079e16ff9a9ba8a272450e81f11f8/pyrefly-0.57.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b01f454fa5539e070c0cba17ddec46b3d2107d571d519bd8eca8f3142ba02a6", size = 34947757, upload-time = "2026-03-18T18:42:17.152Z" }, + { url = "https://files.pythonhosted.org/packages/07/43/f053bf9c65218f70e6a49561e9942c7233f8c3e4da8d42e5fe2aae50b3d2/pyrefly-0.57.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02ad59ea722191f51635f23e37574662116b82ca9d814529f7cb5528f041f381", size = 37621018, upload-time = "2026-03-18T18:42:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/0e/76/9cea46de01665bbc125e4f215340c9365c8d56cda6198ff238a563ea8e75/pyrefly-0.57.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54bc0afe56776145e37733ff763e7e9679ee8a76c467b617dc3f227d4124a9e2", size = 40203649, upload-time = "2026-03-18T18:42:24.519Z" }, + { url = "https://files.pythonhosted.org/packages/fd/8b/2fb4a96d75e2a57df698a43e2970e441ba2704e3906cdc0386a055daa05a/pyrefly-0.57.1-py3-none-win32.whl", hash = "sha256:468e5839144b25bb0dce839bfc5fd879c9f38e68ebf5de561f30bed9ae19d8ca", size = 11732953, upload-time = "2026-03-18T18:42:27.379Z" }, + { url = "https://files.pythonhosted.org/packages/13/5a/4a197910fe2e9b102b15ae5e7687c45b7b5981275a11a564b41e185dd907/pyrefly-0.57.1-py3-none-win_amd64.whl", hash = "sha256:46db9c97093673c4fb7fab96d610e74d140661d54688a92d8e75ad885a56c141", size = 12537319, upload-time = "2026-03-18T18:42:30.196Z" }, + { url = "https://files.pythonhosted.org/packages/b5/c6/bc442874be1d9b63da1f9debb4f04b7d0c590a8dc4091921f3c288207242/pyrefly-0.57.1-py3-none-win_arm64.whl", hash = "sha256:feb1bbe3b0d8d5a70121dcdf1476e6a99cc056a26a49379a156f040729244dcb", size = 12013455, upload-time = "2026-03-18T18:42:32.928Z" }, +] + [[package]] name = "pytest" version = "9.0.2"