Python functions to get top-level importables names
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #1 from astrojuanlu/fix-ci

Fix CI

authored by

Juan Luis Cano Rodríguez and committed by
GitHub
(Dec 4, 2023, 10:23 PM +0100) d775d971 6d24ed37

+92 -53
+1 -1
.github/workflows/rtd-preview.yml
··· 13 13 steps: 14 14 - uses: readthedocs/actions/preview@v1 15 15 with: 16 - project-slug: "pygetpackages" 16 + project-slug: "pygetimportables"
+1 -1
.github/workflows/test.yml
··· 6 6 runs-on: ubuntu-latest 7 7 strategy: 8 8 matrix: 9 - python-version: ["3.7", "3.8", "3.9", "3.10"] 9 + python-version: ["3.9", "3.10", "3.11", "3.12"] 10 10 steps: 11 11 - uses: actions/checkout@v2 12 12 - name: Set up Python ${{ matrix.python-version }}
+1 -1
.readthedocs.yaml
··· 3 3 version: 2 4 4 5 5 build: 6 - os: ubuntu-20.04 6 + os: ubuntu-22.04 7 7 tools: 8 8 python: "3.9" 9 9
+1 -1
CONTRIBUTING.md
··· 2 2 3 3 ## Before contributing 4 4 5 - Welcome to pygetpackages! Before contributing to the project, 5 + Welcome to pygetimportables! Before contributing to the project, 6 6 make sure that you **read our code of conduct** (`CODE_OF_CONDUCT.md`). 7 7 8 8 ## Contributing code
+13 -13
README.md
··· 1 - # pygetpackages 1 + # pygetimportables 2 2 3 - [![Documentation Status](https://readthedocs.org/projects/pygetpackages/badge/?version=latest)](https://pygetpackages.readthedocs.io/en/latest/?badge=latest) 3 + [![Documentation Status](https://readthedocs.org/projects/pygetimportables/badge/?version=latest)](https://pygetimportables.readthedocs.io/en/latest/?badge=latest) 4 4 [![Code style: ruff-format](https://img.shields.io/badge/code%20style-ruff_format-6340ac.svg)](https://github.com/astral-sh/ruff) 5 - [![PyPI](https://img.shields.io/pypi/v/pygetpackages)](https://pypi.org/project/pygetpackages) 5 + [![PyPI](https://img.shields.io/pypi/v/pygetimportables)](https://pypi.org/project/pygetimportables) 6 6 7 - Python functions to get packages from a source tree and an already built wheel. 7 + Python functions to get top-level importable names from a source tree or an already built wheel. 8 8 9 9 See https://discuss.python.org/t/script-to-get-top-level-packages-from-source-tree/40232?u=astrojuanlu 10 10 ··· 13 13 To install, run 14 14 15 15 ``` 16 - (.venv) $ pip install pygetpackages 16 + (.venv) $ pip install pygetimportables 17 17 ``` 18 18 19 19 ## Usage 20 20 21 - To get the packages directly from a source tree: 21 + To get the top-level importable names directly from a source tree: 22 22 23 23 ``` 24 - >>> from pygetpackages import get_packages 25 - >>> get_packages(".") # Wait a few seconds, requires working `pip install` 26 - {'pygetpackages'} 24 + >>> from pygetimportables import get_top_importables 25 + >>> get_top_importables(".") # Wait a few seconds, requires working `pip install` 26 + {'pygetimportables'} 27 27 ``` 28 28 29 - To get packages from an already built wheel: 29 + To get the top-level importable names from an already built wheel: 30 30 31 31 ``` 32 32 (.venv) $ python -m build 33 33 ... 34 34 (.venv) $ python -q 35 - >>> from pygetpackages import get_packages_from_wheel 36 - >>> get_packages_from_wheel("dist/pygetpackages-0.1.0+d20231204-py3-none-any.whl") # Fast 37 - {'pygetpackages'} 35 + >>> from pygetimportables import get_top_importables_from_wheel 36 + >>> get_top_importables_from_wheel("dist/pygetimportables-0.1.0+d20231204-py3-none-any.whl") # Fast 37 + {'pygetimportables'} 38 38 ``` 39 39 40 40 ## Development
+1 -1
docs/source/conf.py
··· 4 4 5 5 # -- Project information 6 6 7 - _metadata = metadata("pygetpackages") 7 + _metadata = metadata("pygetimportables") 8 8 9 9 project = _metadata["Name"] 10 10 author = _metadata["Author-email"].split("<", 1)[0].strip()
+19 -10
pyproject.toml
··· 3 3 build-backend = "pdm.backend" 4 4 5 5 [project] 6 - name = "pygetpackages" 6 + name = "pygetimportables" 7 7 readme = "README.md" 8 - requires-python = ">=3.11" 8 + requires-python = ">=3.9" 9 9 license = {file = "LICENSE"} 10 - description = "Python function that gets packages from a source tree" 10 + description = "Python functions to get top-level importable names" 11 11 dependencies = [ 12 12 "build", 13 + "installer", 13 14 "pyproject_hooks", 15 + "tomli ; python_version < '3.11'", 14 16 "validate-pyproject", 15 17 ] 16 18 authors = [ ··· 23 25 "Operating System :: OS Independent", 24 26 "Programming Language :: Python", 25 27 "Programming Language :: Python :: 3", 28 + "Programming Language :: Python :: 3.9", 29 + "Programming Language :: Python :: 3.10", 26 30 "Programming Language :: Python :: 3.11", 31 + "Programming Language :: Python :: 3.12", 27 32 ] 28 33 dynamic = ["version"] 29 34 30 35 [project.urls] 31 - source = "https://github.com/astrojuanlu/pygetpackages" 32 - tracker = "https://github.com/astrojuanlu/pygetpackages/issues" 33 - documentation = "https://pygetpackages.readthedocs.io" 36 + source = "https://github.com/astrojuanlu/pygetimportables" 37 + tracker = "https://github.com/astrojuanlu/pygetimportables/issues" 38 + documentation = "https://pygetimportables.readthedocs.io" 34 39 35 40 [project.optional-dependencies] 36 41 test = [ ··· 89 94 90 95 # More strict checks for library code 91 96 [[tool.mypy.overrides]] 92 - module = "pygetpackages" 97 + module = "pygetimportables" 93 98 disallow_untyped_defs = true 94 99 95 100 # Ignore certain missing imports 96 - # [[tool.mypy.overrides]] 97 - # module = "thirdparty.*" 98 - # ignore_missing_imports = true 101 + [[tool.mypy.overrides]] 102 + module = "pyproject_hooks.*" 103 + ignore_missing_imports = true 104 + 105 + [[tool.mypy.overrides]] 106 + module = "setuptools.*" 107 + ignore_missing_imports = true 99 108 100 109 [tool.pytest.ini_options] 101 110 testpaths = [
+29 -17
src/pygetpackages/__init__.py src/pygetimportables/__init__.py
··· 1 - """Get the top-level packages of a Python project.""" 1 + """Get the top-level importable names of a Python project.""" 2 2 # The good parts come from https://gist.github.com/pradyunsg/22ca089b48ca55d75ca843a5946b2691 3 3 # Licensed under the MIT license. 4 4 # ··· 22 22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 23 # SOFTWARE. 24 24 25 - import pathlib 25 + from __future__ import annotations 26 + 26 27 import tempfile 27 - import tomllib 28 28 import typing as t 29 29 import zipfile 30 + from pathlib import Path 30 31 31 - from build import ProjectBuilder 32 + try: 33 + import tomllib 34 + except ImportError: 35 + import tomli as tomllib # type: ignore 36 + from build import ConfigSettingsType, ProjectBuilder 32 37 from build.env import DefaultIsolatedEnv 33 38 from installer.sources import WheelFile, WheelSource 34 39 from installer.utils import parse_metadata_file ··· 37 42 38 43 39 44 def _simple_build_wheel( 40 - source_dir, out_dir, *, build_config_settings=None 41 - ) -> WheelFile: 45 + source_dir: str | Path, 46 + out_dir: str | Path, 47 + *, 48 + build_config_settings: ConfigSettingsType | None = None, 49 + ) -> Path: 42 50 """Silently build wheel using build package.""" 43 51 with DefaultIsolatedEnv() as env: 44 52 builder = ProjectBuilder.from_isolated_env( ··· 47 55 env.install(builder.build_system_requires) 48 56 env.install(builder.get_requires_for_build("wheel", build_config_settings)) 49 57 path_built_wheel = builder.build("wheel", out_dir, build_config_settings) 50 - return path_built_wheel 58 + return Path(path_built_wheel) 51 59 52 60 53 61 def _find_importable_components_from_wheel_content_listing( ··· 80 88 return {components[0] for components in importable_components} 81 89 82 90 83 - def _get_importable_components_from_wheel(wheel: WheelSource) -> t.Iterable[str]: 91 + def _get_importable_components_from_wheel( 92 + wheel: WheelSource 93 + ) -> t.Iterable[tuple[str, ...]]: 84 94 metadata = parse_metadata_file(wheel.read_dist_info("WHEEL")) 85 95 if not (metadata["Wheel-Version"] and metadata["Wheel-Version"].startswith("1.")): 86 96 raise NotImplementedError("Only supports wheel 1.x") ··· 95 105 return importable_components 96 106 97 107 98 - def get_packages_from_wheel(wheel_path: str | pathlib.Path): 99 - """Get the top-level packages of a Python project from a wheel file.""" 108 + def get_top_importables_from_wheel(wheel_path: str | Path) -> set[str]: 109 + """Get the top-level importable names of a Python project from a wheel file.""" 100 110 with zipfile.ZipFile(wheel_path, "r") as zf: 101 111 wheel_file = WheelFile(zf) 102 112 importable_components = _get_importable_components_from_wheel(wheel_file) 103 - top_packages = _determine_major_import_names(importable_components) 113 + top_importables = _determine_major_import_names(importable_components) 104 114 105 - return top_packages 115 + return top_importables 106 116 107 117 108 - def get_packages(source_dir, *, build_config_settings=None): 109 - """Get the top-level packages of a Python source tree.""" 110 - pyproject_toml_path = pathlib.Path(source_dir) / "pyproject.toml" 118 + def get_top_importables( 119 + source_dir: str | Path, *, build_config_settings: ConfigSettingsType | None = None 120 + ) -> set[str]: 121 + """Get the top-level importable names of a Python source tree.""" 122 + pyproject_toml_path = Path(source_dir) / "pyproject.toml" 111 123 if not pyproject_toml_path.is_file(): 112 124 raise ValueError( 113 125 "pyproject.toml is missing from source directory, " ··· 131 143 wheel_path = _simple_build_wheel( 132 144 source_dir, outdir, build_config_settings=build_config_settings 133 145 ) 134 - top_packages = get_packages_from_wheel(wheel_path) 146 + top_importables = get_top_importables_from_wheel(wheel_path) 135 147 136 - return top_packages 148 + return top_importables
tests/cases/single_script-0.1.0-py2.py3-none-any.whl

This is a binary file and will not be displayed.

-1
tests/test_import.py
··· 1 - import pygetpackages
+20
tests/test_pygetimportables.py
··· 1 + from pathlib import Path 2 + 3 + import pytest 4 + from pygetimportables import get_top_importables_from_wheel 5 + 6 + here = Path(__file__).parent 7 + 8 + 9 + @pytest.mark.parametrize( 10 + "wheel_path, expected_importables", 11 + [ 12 + ( 13 + here / "cases" / "single_script-0.1.0-py2.py3-none-any.whl", 14 + {"single_script"}, 15 + ), 16 + ], 17 + ) 18 + def test_get_packages_from_wheel(wheel_path, expected_importables): 19 + packages = get_top_importables_from_wheel(wheel_path) 20 + assert packages == expected_importables
+6 -7
tox.ini
··· 2 2 envlist = 3 3 check 4 4 docs 5 - {py37,py38,py39,py310,pypy3}{,-coverage} 5 + {py39,py310,py311,py312,pypy3}{,-coverage} 6 6 # See https://tox.readthedocs.io/en/latest/example/package.html#flit 7 7 isolated_build = True 8 8 isolated_build_env = build 9 9 10 10 [gh-actions] 11 11 python = 12 - 3.6: py36 13 - 3.7: py37 14 - 3.8: py38 15 12 3.9: check, py39 16 13 3.10: py310 14 + 3.11: py311 15 + 3.12: py312 17 16 18 17 [testenv] 19 18 basepython = 20 19 pypy3: pypy3 21 - py37: python3.7 22 - py38: python3.8 23 20 py39: python3.9 24 21 py310: python3.10 22 + py311: python3.11 23 + py312: python3.12 25 24 # See https://github.com/tox-dev/tox/issues/1548 26 25 {check,docs,build}: python3 27 26 setenv = ··· 50 49 [testenv:docs] 51 50 description = build HTML docs 52 51 setenv = 53 - READTHEDOCS_PROJECT = pygetpackages 52 + READTHEDOCS_PROJECT = pygetimportables 54 53 READTHEDOCS_VERSION = latest 55 54 extras = 56 55 doc