diff --git a/.github/workflows/check_version.yml b/.github/workflows/check_version.yml deleted file mode 100644 index 3567c8a..0000000 --- a/.github/workflows/check_version.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Check Release Version - -on: - release: - types: [created, edited, published] - -jobs: - versioncheck: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install setuptools - pip install . - - name: Ensure graphtage.version.DEV_BUILD == False - run: graphtage -dumpversion | grep -qv git diff --git a/.github/workflows/publish_docs.yml b/.github/workflows/publish_docs.yml index 4b32a08..8ce009e 100644 --- a/.github/workflows/publish_docs.yml +++ b/.github/workflows/publish_docs.yml @@ -23,10 +23,10 @@ jobs: env: # The use of ::set-env here is safe! ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' - - name: Set up Python 3.8 + - name: Set up Python 3.12 uses: actions/setup-python@v5 with: - python-version: 3.8 + python-version: 3.12 - name: Install dependencies run: | cd graphtage diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index d29d662..d1cdda5 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.8, 3.9, "3.10", "3.11"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index 4f25c74..55fda0a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,21 @@ -__pycache__ -.cache +__pycache__/ +.cache/ .python_history *.pyc +*.pyo +*.pyd +.Python build/ dist/ -graphtage.egg-info +*.egg-info/ +.eggs/ +.ruff_cache/ +.pytest_cache/ +.mypy_cache/ +.coverage +htmlcov/ +.tox/ +.venv/ +venv/ +ENV/ +env/ diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 761389f..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include LICENSE -recursive-include test *.* diff --git a/graphtage/expressions.py b/graphtage/expressions.py index 5f09c2a..12ce225 100644 --- a/graphtage/expressions.py +++ b/graphtage/expressions.py @@ -48,7 +48,7 @@ obj.__name__: obj for obj in ( str, bool, int, bytes, float, bytearray, dict, set, frozenset, enumerate, zip, map, filter, any, all, chr, ord, abs, ascii, bin, bool, complex, hash, hex, oct, min, max, id, - iter, len, list, slice, sorted, sum, tuple, round + iter, len, list, slice, sorted, sum, tuple, round, pow, divmod, repr, isinstance, issubclass, range, reversed ) } diff --git a/graphtage/version.py b/graphtage/version.py index a9fbda7..323a737 100644 --- a/graphtage/version.py +++ b/graphtage/version.py @@ -1,86 +1,14 @@ """A module that centralizes the version information for Graphtage. Changing the version here not only affects the version printed with the ``--version`` command line option, but it also -automatically updates the version used in ``setup.py`` and rendered in the documentation. - -Attributes: - DEV_BUILD (bool): Sets whether this build is a development build. - This should only be set to :const:`True` to coincide with a release. It should *always* be :const:`True` before - deploying to PyPI. - - If :const:`False`, the git branch will be included in :attr:`graphtage.version.__version__`. - - __version__ (Tuple[Union[int, str], ...]): The version of Graphtage. This tuple can contain any sequence of ints and - strings. Typically this will be three ints: major/minor/revision number. However, it can contain additional - ints and strings. If :attr:`graphtage.version.DEV_BUILD`, then `("git", git_branch())` will be appended to the - version. - - VERSION_STRING (str): A rendered string containing the version of Graphtage. Each element of - :attr:`graphtage.version.__version__` is appended to the string, delimited by a "." if the element is an ``int`` - or a "-" if the element is a string. - +automatically updates the version used in the build system and rendered in the documentation. """ -import os -import subprocess -from typing import Optional, Tuple, Union - - -def git_branch() -> Optional[str]: - """Returns the git branch for the codebase, or :const:`None` if it could not be determined. - - The git branch is determined by running - - .. code-block:: console - - $ git symbolic-ref -q HEAD - - """ - try: - branch = subprocess.check_output( - ['git', 'symbolic-ref', '-q', 'HEAD'], - cwd=os.path.dirname(os.path.realpath(__file__)), - stderr=subprocess.DEVNULL - ) - branch = branch.decode('utf-8').strip().split('/')[-1] - return branch - except Exception: - return None - - -DEV_BUILD = False -"""Sets whether this build is a development build. - -This should only be set to :const:`False` to coincide with a release. It should *always* be :const:`False` before -deploying to PyPI. - -If :const:`True`, the git branch will be included in the version string. - -""" - - -__version__: Tuple[Union[int, str], ...] = (0, 3, 1) - -if DEV_BUILD: - branch_name = git_branch() - if branch_name is None: - __version__ = __version__ + ('git',) - else: - __version__ = __version__ + ('git', branch_name) - -VERSION_STRING = '' +__version__ = "0.3.1" +VERSION_STRING = __version__ -for element in __version__: - if isinstance(element, int): - if VERSION_STRING: - VERSION_STRING += f'.{element}' - else: - VERSION_STRING = str(element) - else: - if VERSION_STRING: - VERSION_STRING += f'-{element!s}' - else: - VERSION_STRING += str(element) +# For backwards compatibility with code that expects the tuple format +__version_tuple__ = tuple(int(x) if x.isdigit() else x for x in __version__.split('.')) if __name__ == '__main__': diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..318f881 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,116 @@ +[build-system] +requires = ["hatchling>=1.21.0", "hatch-vcs>=0.4.0"] +build-backend = "hatchling.build" + +[project] +name = "graphtage" +description = "A utility to diff tree-like files such as JSON and XML." +readme = "README.md" +requires-python = ">=3.8" +license = {text = "LGPL-3.0-or-later"} +authors = [ + {name = "Trail of Bits"}, +] +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Utilities", +] +dependencies = [ + "colorama", + "fickling>=0.1.3", + "intervaltree", + "json5==0.9.5", + "numpy>=1.19.4", + "PyYAML", + "scipy>=1.4.0", + "tqdm", + "typing_extensions>=3.7.4.3", +] +dynamic = ["version"] + +[project.optional-dependencies] +dev = [ + "pytest>=7.0.0", + "ruff>=0.1.0", + "sphinx>=5.0.0", + "sphinx_rtd_theme==3.0.2", + "twine>=4.0.0", +] + +[project.scripts] +graphtage = "graphtage.__main__:main" + +[project.urls] +Homepage = "https://github.com/trailofbits/graphtage" +Documentation = "https://trailofbits.github.io/graphtage" +Issues = "https://github.com/trailofbits/graphtage/issues" +Source = "https://github.com/trailofbits/graphtage" + +[tool.hatch.version] +path = "graphtage/version.py" + +[tool.hatch.build] +include = [ + "graphtage/**/*.py", + "LICENSE", + "README.md", +] + +[tool.hatch.build.targets.sdist] +include = [ + "graphtage/**/*.py", + "test/**/*.py", + "LICENSE", + "README.md", + "pyproject.toml", +] + +[tool.hatch.build.targets.wheel] +packages = ["graphtage"] + +[tool.ruff] +target-version = "py38" +line-length = 120 +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "UP", # pyupgrade + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "SIM", # flake8-simplify + "TID", # flake8-tidy-imports + "RUF", # Ruff-specific rules +] +ignore = [ + "E501", # line too long (handled by formatter) + "B008", # do not perform function calls in argument defaults + "B904", # raise without from inside except + "SIM108", # use ternary operator instead of if-else block +] + +[tool.ruff.per-file-ignores] +"test/**/*.py" = [ + "F401", # unused imports in tests + "F841", # unused variables in tests +] + +[tool.ruff.isort] +known-first-party = ["graphtage"] + +[tool.pytest.ini_options] +testpaths = ["test"] +python_files = "test_*.py" +python_classes = "Test*" +python_functions = "test_*" \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 74a3bf3..0000000 --- a/setup.py +++ /dev/null @@ -1,74 +0,0 @@ -import os -from setuptools import setup, find_packages - -HERE = os.path.realpath(os.path.dirname(__file__)) - -VERSION_MODULE_PATH = os.path.join(HERE, "graphtage", "version.py") -README_PATH = os.path.join(HERE, "README.md") - - -def get_version_string(): - version = {} - with open(VERSION_MODULE_PATH) as f: - exec(f.read(), version) - return version['VERSION_STRING'] - - -def get_readme(): - with open(README_PATH, encoding='utf-8') as f: - return f.read() - - -setup( - name='graphtage', - description='A utility to diff tree-like files such as JSON and XML.', - license="LGPL-3.0-or-later", - long_description=get_readme(), - long_description_content_type="text/markdown", - url='https://github.com/trailofbits/graphtage', - project_urls={ - 'Documentation': 'https://trailofbits.github.io/graphtage', - 'Source': 'https://github.com/trailofbits/graphtage', - 'Tracker': 'https://github.com/trailofbits/graphtage/issues', - }, - author='Trail of Bits', - version=get_version_string(), - packages=find_packages(exclude=['test']), - python_requires='>=3.8', - install_requires=[ - "colorama", - "fickling>=0.1.3", - "intervaltree", - "json5==0.9.5", - "numpy>=1.19.4", - "PyYAML", - "scipy>=1.4.0", - "tqdm", - "typing_extensions>=3.7.4.3" - ], - entry_points={ - 'console_scripts': [ - 'graphtage = graphtage.__main__:main' - ] - }, - extras_require={ - "dev": [ - "flake8", - "Sphinx", - "pytest", - "sphinx_rtd_theme==1.2.2", - "twine", - # workaround for https://github.com/python/importlib_metadata/issues/406: - "importlib_metadata<5; python_version == '3.7'" - ] - }, - classifiers=[ - 'Development Status :: 4 - Beta', - 'Environment :: Console', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)', - 'Programming Language :: Python :: 3 :: Only', - 'Topic :: Utilities' - ], - include_package_data=True -)