Name | pyproj-inspector JSON |
Version |
0.1.2
JSON |
| download |
home_page | None |
Summary | Analyze and package Python projects |
upload_time | 2025-09-11 03:25:21 |
maintainer | None |
docs_url | None |
author | Avi Twil |
requires_python | >=3.8 |
license | MIT |
keywords |
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# pyproj_inspector
Analyze a Python script or project, classify imports, reconstruct sources, and quickly package into distributables.
Authored by **Avi Twil**.
> Version: `0.1.2` • License: MIT • Python: 3.8+ • OS: Windows, macOS, Linux
---
## Table of Contents
- [Overview](#overview)
- [Key Features](#key-features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Analyze a single script](#analyze-a-single-script)
- [Analyze a project directory](#analyze-a-project-directory)
- [Python API](#python-api)
- [`PythonProject`](#pythonproject)
- [`ProjectParseResult`](#projectparseresult)
- [High-level Methods](#high-level-methods)
- [`moduls()`](#moduls)
- [`restore_to(target)`](#restore_totarget)
- [`run_in_tmp_env(...)`](#run_in_tmp_env)
- [Build Utilities](#build-utilities)
- [`create_binary(...)`](#create_binary)
- [Packaging Utilities](#packaging-utilities)
- [`create_pypi_package(...)`](#create_pypi_package)
- [`plan_pypi_version(...)`](#plan_pypi_version)
- [`create_debian_package(...)`](#create_debian_package)
- [CLI Usage](#cli-usage)
- [How it works (Design Notes)](#how-it-works-design-notes)
- [Limitations & Notes](#limitations--notes)
- [Troubleshooting](#troubleshooting)
- [Development](#development)
- [Project Layout](#project-layout)
- [Run Tests](#run-tests)
- [Changelog](#changelog)
- [Author](#author)
- [License](#license)
---
## Overview
`pyproj_inspector` ingests either a **single Python file** or a **project directory**, parses all `.py` files, and builds a structured view of your codebase:
- Built-in (stdlib) imports
- External imports (PyPI distributions mapped from import names)
- Internal modules (top-level packages/modules contained in your project)
- A map of `relative_path -> source_code` for every file
- An optional entry script when a single file is analyzed
It also ships with utilities to:
- Materialize the analyzed project into a temporary or target directory
- Create binaries via **PyInstaller** or **Nuitka**
- Generate a ready-to-edit **`pyproject.toml`** for packaging to PyPI
- Build a Debian `.deb` package (when `dpkg-deb` is available)
---
## Key Features
- ⚙️ **Static analysis** via `ast` – resilient even if some files have syntax errors
- 🏷️ **Import classification**:
- **Builtins** using `sys.stdlib_module_names` (fallback list included)
- **External** mapped via `importlib.metadata.packages_distributions()` with a fallback **PyPI Simple** `HEAD` probe per import
- **Internal** detected from **actual files** (even if not imported) and from relative imports
- 🧳 **Rehydration** – write all sources back to disk with original relative paths
- 🚀 **Ephemeral execution** – run your entry script in a fresh venv, optionally installing external deps
- 📦 **Packaging helpers** – binary via **PyInstaller**/**Nuitka**, **PyPI** metadata scaffold, **Debian** packages
- 🧪 **Comprehensive tests** – unit & edge-case coverage
---
## Installation
```bash
# From your project (editable install)
pip install -e .
# Or install from a wheel/sdist you build later
pip install pyproj_inspector-*.whl
```
> For **binary creation**, ensure you have the chosen tool installed in your environment:
> - PyInstaller: `pip install pyinstaller`
> - Nuitka: `pip install nuitka`
>
> For **Debian packaging**, you need `dpkg-deb` available on your system.
---
## Quick Start
### Analyze a single script
```python
from pyproj_inspector import PythonProject
proj = PythonProject("path/to/app.py")
print(proj.result.builtins) # {'os', 'json', ...}
print(proj.result.external_imports) # {'requests': {'requests'}, ...}
print(proj.result.internal_modules) # {'app'}
print(proj.result.entry_relpath) # 'app.py'
```
### Analyze a project directory
```python
from pyproj_inspector import PythonProject
proj = PythonProject("path/to/project_dir")
print(sorted(proj.moduls())) # e.g. ['pkg', 'utils']
print(len(proj.result.files_code)) # number of .py files discovered
```
---
## Python API
### `PythonProject`
```python
PythonProject(path: str | os.PathLike)
```
- **`path`**: A `.py` file or a project directory.
- If a single file is passed, `entry_relpath` is set to its filename.
> Parsing is robust: files with syntax errors are still captured in `files_code` and simply skipped for AST import extraction.
### `ProjectParseResult`
```python
@dataclass
class ProjectParseResult:
root: Path
builtins: Set[str]
external_imports: Dict[str, Set[str]] # distribution -> {import names}
internal_modules: Set[str]
files_code: Dict[str, str] # 'relative/path.py' -> source
entry_relpath: Optional[str] # when analyzing a single file
```
- **Builtins**: stdlib modules detected via `sys.stdlib_module_names` or a curated fallback set.
- **External imports**: resolved via `packages_distributions()`. Any unmapped names are *probingly* tested against PyPI’s simple index (`HEAD`) to guess a matching distribution (best effort).
- **Internal modules**: determined by the project’s file layout (top-level names from `*.py` and package dirs) + relative-import hints. For packages, `pkg/__init__.py` is reported as `pkg`.
### High-level Methods
#### `moduls()`
```python
proj.moduls() -> List[str]
```
Returns a **sorted** list of internal module names. This reflects the top-level modules/packages detected in your project’s tree (e.g., `['app', 'utils']`).
#### `restore_to(target)`
```python
proj.restore_to("out/dir") -> pathlib.Path
```
Writes every captured file from `files_code` to the given directory, preserving relative paths. It **overwrites** existing files.
#### `run_in_tmp_env(...)`
```python
proj.run_in_tmp_env(
entry: Optional[str] = None,
install: bool = True,
env: Optional[Dict[str, str]] = None,
args: Optional[List[str]] = None,
python: Optional[str] = None,
) -> subprocess.CompletedProcess
```
- Creates a temp directory, restores all sources, bootstraps a virtual environment and **optionally installs external distributions** (keys from `external_imports`).
- **`entry`**: by default uses `entry_relpath` (if analyzing a single file). If missing, falls back to `__main__.py` or `main.py` when present.
- Returns a `CompletedProcess` with `stdout`, `stderr`, and `returncode`.
> Useful for quick smoke tests in isolation.
### Build Utilities
#### `create_binary(...)`
```python
from pyproj_inspector import create_binary
create_binary(
project_root: str | os.PathLike,
entry: str,
mode: Literal["pyinstaller", "nuitka"] = "pyinstaller",
onefile: bool = True,
output_dir: Optional[str | os.PathLike] = None,
extra_args: Optional[list[str]] = None,
) -> pathlib.Path
```
- Builds a standalone binary of `entry` using **PyInstaller** or **Nuitka**.
- Returns the path to the produced artifact.
- **Requirements**: the chosen backend must be installed and available in the current Python environment.
### Packaging Utilities
#### `create_pypi_package(...)`
```python
from pyproj_inspector import create_pypi_package
create_pypi_package(
project_root: str | Path,
package_name: str,
version: Optional[str] = None,
new: bool = True,
creator_name: str = "Unknown",
description: str = "Auto-generated package",
homepage: str = "",
) -> Path
```
- Writes a **`pyproject.toml`** scaffold (PEP 621) and creates a package directory with `__init__.py`.
- When `new=True`, checks PyPI for name availability; if taken, raises `ValueError`.
- When `new=False`, fetches the latest published version and **bumps the patch** (e.g., `1.0.0 -> 1.0.1`), unless you pass a **higher** `version`, which takes precedence.
> The template is rendered using `string.Template` to avoid brace-related issues.
#### `plan_pypi_version(...)`
```python
from pyproj_inspector.packaging_utils import plan_pypi_version
plan = plan_pypi_version(name, version, new)
print(plan.name, plan.version, plan.is_new_project)
```
- Returns the chosen name/version and whether this is treated as a new project.
#### `create_debian_package(...)`
```python
from pyproj_inspector import packaging_utils
packaging_utils.create_debian_package(
project_root: str | Path,
package_name: str,
version: str = "0.1.0",
creator_name: str = "Unknown",
entry: Optional[str] = None,
) -> Path
```
- Produces a Debian `.deb` by staging the project under `/usr/local/lib/<name>` with a basic `DEBIAN/control` file.
- If `entry` is provided, a launcher script is placed under `/usr/local/bin/<name>`.
- **Requirement**: `dpkg-deb` must be available.
---
## CLI Usage
```bash
# Print JSON analysis (builtins/external/internal/files)
pyproj_inspector <PATH> --json
# Build a binary
pyproj_inspector <PATH> binary --entry main.py --mode pyinstaller --onefile
# Scaffold PyPI packaging (pyproject.toml)
pyproj_inspector <PATH> pypi --name my_project --new --creator "Avi Twil"
# Build a Debian package
pyproj_inspector <PATH> deb --name my_project --version 0.1.0 --creator "Avi Twil" --entry main.py
```
> On Windows via PyCharm’s terminal, ensure the active interpreter has the required backend (e.g., `pip install pyinstaller`).
---
## How it works (Design Notes)
1. **Discovery**
- If `path` is a file: analyze just that file; set `entry_relpath` to its name.
- If `path` is a directory: recursively collect `*.py` files.
2. **File capture**
- Read each file as UTF‑8, falling back to Latin‑1 when needed. All sources are stored in `files_code`.
3. **Internal modules**
- Derived from file layout (**top-level** names of `*.py` and package directories).
- `pkg/__init__.py` normalizes to `pkg`.
- Relative imports (e.g. `from . import x`) mark the current package as internal too.
4. **Import classification**
- Parse each file’s AST; collect top-level import names.
- Classify priority: **internal (if a file/dir exists)** → **stdlib** → **external**.
5. **External mapping**
- Use `packages_distributions()` when available.
- For unmapped names, probe **PyPI Simple** `HEAD` (`https://pypi.org/simple/<name>/`) to infer a plausible distribution name.
6. **Execution sandbox**
- `run_in_tmp_env()` creates an isolated venv, installs external distributions (if any), and executes the chosen entry script.
---
## Limitations & Notes
- **Namespace packages (PEP 420)**: currently not fully supported. A top-level directory without `__init__.py` may not always be recognized as a package.
_Planned_: heuristic support to treat existing directories as internal packages when imported.
- **External mapping is best-effort**: PyPI probing is a heuristic; unusual naming may require manual intervention.
- **Binary size/behavior**: depends on the chosen backend (PyInstaller/Nuitka) and your project’s specifics.
- **Network access**: PyPI checks require connectivity unless you inject your own mapping logic.
---
## Troubleshooting
- **`ValueError: Project name 'X' already exists on PyPI`**
Use a different `--name` or set `new=False` and pass a higher `--version`.
- **`FileNotFoundError` when running**
Ensure `entry` exists **in the analyzed sources** (`files_code`); if you analyze a directory, add the script first.
- **Binary build fails**
Verify the backend is installed: `pip show pyinstaller` / `pip show nuitka`. Check platform-specific notes of those tools.
- **Imports misclassified**
If you intentionally shadow stdlib (e.g., `json.py`), the tool prioritizes **internal** over stdlib—this is by design.
---
## Development
### Project Layout
```
pyproj_inspector/
__init__.py
inspector.py # analysis core
build_utils.py # binary builders
packaging_utils.py # PyPI/DEB helpers
cli.py # command-line interface
tests/
... # unit & edge-case tests
pyproject.toml
README.md
```
### Run Tests
```bash
python -m pip install -U pip pytest
pytest -q
# or on Windows:
run_tests.bat
```
---
## Changelog
### 0.1.2
- Fix `pyproject.toml` template rendering using `string.Template`
- Normalize `pkg/__init__.py` → `pkg`
- Prefer **internal** over stdlib for shadowed names (e.g., local `json.py`)
- Register internal modules from file layout (even if not imported)
- CLI imports modules for easier monkeypatching in tests
---
## Author
**Avi Twil**
---
## License
MIT © Avi Twil
Raw data
{
"_id": null,
"home_page": null,
"name": "pyproj-inspector",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": null,
"author": "Avi Twil",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/5b/7a/0903fa37262476a4906db2979178f3ad1ebad911c6e0252a6964b4330956/pyproj_inspector-0.1.2.tar.gz",
"platform": null,
"description": "\r\n# pyproj_inspector\r\n\r\nAnalyze a Python script or project, classify imports, reconstruct sources, and quickly package into distributables.\r\nAuthored by **Avi Twil**.\r\n\r\n> Version: `0.1.2` \u2022 License: MIT \u2022 Python: 3.8+ \u2022 OS: Windows, macOS, Linux\r\n\r\n---\r\n\r\n## Table of Contents\r\n\r\n- [Overview](#overview)\r\n- [Key Features](#key-features)\r\n- [Installation](#installation)\r\n- [Quick Start](#quick-start)\r\n - [Analyze a single script](#analyze-a-single-script)\r\n - [Analyze a project directory](#analyze-a-project-directory)\r\n- [Python API](#python-api)\r\n - [`PythonProject`](#pythonproject)\r\n - [`ProjectParseResult`](#projectparseresult)\r\n - [High-level Methods](#high-level-methods)\r\n - [`moduls()`](#moduls)\r\n - [`restore_to(target)`](#restore_totarget)\r\n - [`run_in_tmp_env(...)`](#run_in_tmp_env)\r\n - [Build Utilities](#build-utilities)\r\n - [`create_binary(...)`](#create_binary)\r\n - [Packaging Utilities](#packaging-utilities)\r\n - [`create_pypi_package(...)`](#create_pypi_package)\r\n - [`plan_pypi_version(...)`](#plan_pypi_version)\r\n - [`create_debian_package(...)`](#create_debian_package)\r\n- [CLI Usage](#cli-usage)\r\n- [How it works (Design Notes)](#how-it-works-design-notes)\r\n- [Limitations & Notes](#limitations--notes)\r\n- [Troubleshooting](#troubleshooting)\r\n- [Development](#development)\r\n - [Project Layout](#project-layout)\r\n - [Run Tests](#run-tests)\r\n- [Changelog](#changelog)\r\n- [Author](#author)\r\n- [License](#license)\r\n\r\n---\r\n\r\n## Overview\r\n\r\n`pyproj_inspector` ingests either a **single Python file** or a **project directory**, parses all `.py` files, and builds a structured view of your codebase:\r\n\r\n- Built-in (stdlib) imports\r\n- External imports (PyPI distributions mapped from import names)\r\n- Internal modules (top-level packages/modules contained in your project)\r\n- A map of `relative_path -> source_code` for every file\r\n- An optional entry script when a single file is analyzed\r\n\r\nIt also ships with utilities to:\r\n\r\n- Materialize the analyzed project into a temporary or target directory\r\n- Create binaries via **PyInstaller** or **Nuitka**\r\n- Generate a ready-to-edit **`pyproject.toml`** for packaging to PyPI\r\n- Build a Debian `.deb` package (when `dpkg-deb` is available)\r\n\r\n---\r\n\r\n## Key Features\r\n\r\n- \u2699\ufe0f **Static analysis** via `ast` \u2013 resilient even if some files have syntax errors\r\n- \ud83c\udff7\ufe0f **Import classification**:\r\n - **Builtins** using `sys.stdlib_module_names` (fallback list included)\r\n - **External** mapped via `importlib.metadata.packages_distributions()` with a fallback **PyPI Simple** `HEAD` probe per import\r\n - **Internal** detected from **actual files** (even if not imported) and from relative imports\r\n- \ud83e\uddf3 **Rehydration** \u2013 write all sources back to disk with original relative paths\r\n- \ud83d\ude80 **Ephemeral execution** \u2013 run your entry script in a fresh venv, optionally installing external deps\r\n- \ud83d\udce6 **Packaging helpers** \u2013 binary via **PyInstaller**/**Nuitka**, **PyPI** metadata scaffold, **Debian** packages\r\n- \ud83e\uddea **Comprehensive tests** \u2013 unit & edge-case coverage\r\n\r\n---\r\n\r\n## Installation\r\n\r\n```bash\r\n# From your project (editable install)\r\npip install -e .\r\n\r\n# Or install from a wheel/sdist you build later\r\npip install pyproj_inspector-*.whl\r\n```\r\n\r\n> For **binary creation**, ensure you have the chosen tool installed in your environment:\r\n> - PyInstaller: `pip install pyinstaller`\r\n> - Nuitka: `pip install nuitka`\r\n>\r\n> For **Debian packaging**, you need `dpkg-deb` available on your system.\r\n\r\n---\r\n\r\n## Quick Start\r\n\r\n### Analyze a single script\r\n\r\n```python\r\nfrom pyproj_inspector import PythonProject\r\n\r\nproj = PythonProject(\"path/to/app.py\")\r\nprint(proj.result.builtins) # {'os', 'json', ...}\r\nprint(proj.result.external_imports) # {'requests': {'requests'}, ...}\r\nprint(proj.result.internal_modules) # {'app'}\r\nprint(proj.result.entry_relpath) # 'app.py'\r\n```\r\n\r\n### Analyze a project directory\r\n\r\n```python\r\nfrom pyproj_inspector import PythonProject\r\n\r\nproj = PythonProject(\"path/to/project_dir\")\r\nprint(sorted(proj.moduls())) # e.g. ['pkg', 'utils']\r\nprint(len(proj.result.files_code)) # number of .py files discovered\r\n```\r\n\r\n---\r\n\r\n## Python API\r\n\r\n### `PythonProject`\r\n\r\n```python\r\nPythonProject(path: str | os.PathLike)\r\n```\r\n- **`path`**: A `.py` file or a project directory.\r\n- If a single file is passed, `entry_relpath` is set to its filename.\r\n\r\n> Parsing is robust: files with syntax errors are still captured in `files_code` and simply skipped for AST import extraction.\r\n\r\n### `ProjectParseResult`\r\n\r\n```python\r\n@dataclass\r\nclass ProjectParseResult:\r\n root: Path\r\n builtins: Set[str]\r\n external_imports: Dict[str, Set[str]] # distribution -> {import names}\r\n internal_modules: Set[str]\r\n files_code: Dict[str, str] # 'relative/path.py' -> source\r\n entry_relpath: Optional[str] # when analyzing a single file\r\n```\r\n- **Builtins**: stdlib modules detected via `sys.stdlib_module_names` or a curated fallback set.\r\n- **External imports**: resolved via `packages_distributions()`. Any unmapped names are *probingly* tested against PyPI\u2019s simple index (`HEAD`) to guess a matching distribution (best effort).\r\n- **Internal modules**: determined by the project\u2019s file layout (top-level names from `*.py` and package dirs) + relative-import hints. For packages, `pkg/__init__.py` is reported as `pkg`.\r\n\r\n### High-level Methods\r\n\r\n#### `moduls()`\r\n\r\n```python\r\nproj.moduls() -> List[str]\r\n```\r\nReturns a **sorted** list of internal module names. This reflects the top-level modules/packages detected in your project\u2019s tree (e.g., `['app', 'utils']`).\r\n\r\n#### `restore_to(target)`\r\n\r\n```python\r\nproj.restore_to(\"out/dir\") -> pathlib.Path\r\n```\r\nWrites every captured file from `files_code` to the given directory, preserving relative paths. It **overwrites** existing files.\r\n\r\n#### `run_in_tmp_env(...)`\r\n\r\n```python\r\nproj.run_in_tmp_env(\r\n entry: Optional[str] = None,\r\n install: bool = True,\r\n env: Optional[Dict[str, str]] = None,\r\n args: Optional[List[str]] = None,\r\n python: Optional[str] = None,\r\n) -> subprocess.CompletedProcess\r\n```\r\n- Creates a temp directory, restores all sources, bootstraps a virtual environment and **optionally installs external distributions** (keys from `external_imports`).\r\n- **`entry`**: by default uses `entry_relpath` (if analyzing a single file). If missing, falls back to `__main__.py` or `main.py` when present.\r\n- Returns a `CompletedProcess` with `stdout`, `stderr`, and `returncode`.\r\n\r\n> Useful for quick smoke tests in isolation.\r\n\r\n### Build Utilities\r\n\r\n#### `create_binary(...)`\r\n\r\n```python\r\nfrom pyproj_inspector import create_binary\r\n\r\ncreate_binary(\r\n project_root: str | os.PathLike,\r\n entry: str,\r\n mode: Literal[\"pyinstaller\", \"nuitka\"] = \"pyinstaller\",\r\n onefile: bool = True,\r\n output_dir: Optional[str | os.PathLike] = None,\r\n extra_args: Optional[list[str]] = None,\r\n) -> pathlib.Path\r\n```\r\n- Builds a standalone binary of `entry` using **PyInstaller** or **Nuitka**.\r\n- Returns the path to the produced artifact.\r\n- **Requirements**: the chosen backend must be installed and available in the current Python environment.\r\n\r\n### Packaging Utilities\r\n\r\n#### `create_pypi_package(...)`\r\n\r\n```python\r\nfrom pyproj_inspector import create_pypi_package\r\n\r\ncreate_pypi_package(\r\n project_root: str | Path,\r\n package_name: str,\r\n version: Optional[str] = None,\r\n new: bool = True,\r\n creator_name: str = \"Unknown\",\r\n description: str = \"Auto-generated package\",\r\n homepage: str = \"\",\r\n) -> Path\r\n```\r\n- Writes a **`pyproject.toml`** scaffold (PEP 621) and creates a package directory with `__init__.py`.\r\n- When `new=True`, checks PyPI for name availability; if taken, raises `ValueError`.\r\n- When `new=False`, fetches the latest published version and **bumps the patch** (e.g., `1.0.0 -> 1.0.1`), unless you pass a **higher** `version`, which takes precedence.\r\n\r\n> The template is rendered using `string.Template` to avoid brace-related issues.\r\n\r\n#### `plan_pypi_version(...)`\r\n\r\n```python\r\nfrom pyproj_inspector.packaging_utils import plan_pypi_version\r\n\r\nplan = plan_pypi_version(name, version, new)\r\nprint(plan.name, plan.version, plan.is_new_project)\r\n```\r\n- Returns the chosen name/version and whether this is treated as a new project.\r\n\r\n#### `create_debian_package(...)`\r\n\r\n```python\r\nfrom pyproj_inspector import packaging_utils\r\npackaging_utils.create_debian_package(\r\n project_root: str | Path,\r\n package_name: str,\r\n version: str = \"0.1.0\",\r\n creator_name: str = \"Unknown\",\r\n entry: Optional[str] = None,\r\n) -> Path\r\n```\r\n- Produces a Debian `.deb` by staging the project under `/usr/local/lib/<name>` with a basic `DEBIAN/control` file.\r\n- If `entry` is provided, a launcher script is placed under `/usr/local/bin/<name>`.\r\n- **Requirement**: `dpkg-deb` must be available.\r\n\r\n---\r\n\r\n## CLI Usage\r\n\r\n```bash\r\n# Print JSON analysis (builtins/external/internal/files)\r\npyproj_inspector <PATH> --json\r\n\r\n# Build a binary\r\npyproj_inspector <PATH> binary --entry main.py --mode pyinstaller --onefile\r\n\r\n# Scaffold PyPI packaging (pyproject.toml)\r\npyproj_inspector <PATH> pypi --name my_project --new --creator \"Avi Twil\"\r\n\r\n# Build a Debian package\r\npyproj_inspector <PATH> deb --name my_project --version 0.1.0 --creator \"Avi Twil\" --entry main.py\r\n```\r\n\r\n> On Windows via PyCharm\u2019s terminal, ensure the active interpreter has the required backend (e.g., `pip install pyinstaller`).\r\n\r\n---\r\n\r\n## How it works (Design Notes)\r\n\r\n1. **Discovery** \r\n - If `path` is a file: analyze just that file; set `entry_relpath` to its name.\r\n - If `path` is a directory: recursively collect `*.py` files.\r\n\r\n2. **File capture** \r\n - Read each file as UTF\u20118, falling back to Latin\u20111 when needed. All sources are stored in `files_code`.\r\n\r\n3. **Internal modules** \r\n - Derived from file layout (**top-level** names of `*.py` and package directories). \r\n - `pkg/__init__.py` normalizes to `pkg`. \r\n - Relative imports (e.g. `from . import x`) mark the current package as internal too.\r\n\r\n4. **Import classification** \r\n - Parse each file\u2019s AST; collect top-level import names. \r\n - Classify priority: **internal (if a file/dir exists)** \u2192 **stdlib** \u2192 **external**.\r\n\r\n5. **External mapping** \r\n - Use `packages_distributions()` when available. \r\n - For unmapped names, probe **PyPI Simple** `HEAD` (`https://pypi.org/simple/<name>/`) to infer a plausible distribution name.\r\n\r\n6. **Execution sandbox** \r\n - `run_in_tmp_env()` creates an isolated venv, installs external distributions (if any), and executes the chosen entry script.\r\n\r\n---\r\n\r\n## Limitations & Notes\r\n\r\n- **Namespace packages (PEP 420)**: currently not fully supported. A top-level directory without `__init__.py` may not always be recognized as a package. \r\n _Planned_: heuristic support to treat existing directories as internal packages when imported.\r\n- **External mapping is best-effort**: PyPI probing is a heuristic; unusual naming may require manual intervention.\r\n- **Binary size/behavior**: depends on the chosen backend (PyInstaller/Nuitka) and your project\u2019s specifics.\r\n- **Network access**: PyPI checks require connectivity unless you inject your own mapping logic.\r\n\r\n---\r\n\r\n## Troubleshooting\r\n\r\n- **`ValueError: Project name 'X' already exists on PyPI`** \r\n Use a different `--name` or set `new=False` and pass a higher `--version`.\r\n- **`FileNotFoundError` when running** \r\n Ensure `entry` exists **in the analyzed sources** (`files_code`); if you analyze a directory, add the script first.\r\n- **Binary build fails** \r\n Verify the backend is installed: `pip show pyinstaller` / `pip show nuitka`. Check platform-specific notes of those tools.\r\n- **Imports misclassified** \r\n If you intentionally shadow stdlib (e.g., `json.py`), the tool prioritizes **internal** over stdlib\u2014this is by design.\r\n\r\n---\r\n\r\n## Development\r\n\r\n### Project Layout\r\n\r\n```\r\npyproj_inspector/\r\n __init__.py\r\n inspector.py # analysis core\r\n build_utils.py # binary builders\r\n packaging_utils.py # PyPI/DEB helpers\r\n cli.py # command-line interface\r\ntests/\r\n ... # unit & edge-case tests\r\npyproject.toml\r\nREADME.md\r\n```\r\n\r\n### Run Tests\r\n\r\n```bash\r\npython -m pip install -U pip pytest\r\npytest -q\r\n# or on Windows:\r\nrun_tests.bat\r\n```\r\n\r\n---\r\n\r\n## Changelog\r\n\r\n### 0.1.2\r\n- Fix `pyproject.toml` template rendering using `string.Template`\r\n- Normalize `pkg/__init__.py` \u2192 `pkg`\r\n- Prefer **internal** over stdlib for shadowed names (e.g., local `json.py`)\r\n- Register internal modules from file layout (even if not imported)\r\n- CLI imports modules for easier monkeypatching in tests\r\n\r\n---\r\n\r\n## Author\r\n\r\n**Avi Twil**\r\n\r\n---\r\n\r\n## License\r\n\r\nMIT \u00a9 Avi Twil\r\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Analyze and package Python projects",
"version": "0.1.2",
"project_urls": null,
"split_keywords": [],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "4af26f90148362826a8a9fb8a36fd12ba29b9141c25cce42ac44c0227d90c857",
"md5": "134fa7b4bbf27edbcaffc127db0c56ff",
"sha256": "9f135f1f1c9acc09c46f06b296d776546746c0850b03b25a0ede2359644ab73f"
},
"downloads": -1,
"filename": "pyproj_inspector-0.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "134fa7b4bbf27edbcaffc127db0c56ff",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 13129,
"upload_time": "2025-09-11T03:25:19",
"upload_time_iso_8601": "2025-09-11T03:25:19.991027Z",
"url": "https://files.pythonhosted.org/packages/4a/f2/6f90148362826a8a9fb8a36fd12ba29b9141c25cce42ac44c0227d90c857/pyproj_inspector-0.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "5b7a0903fa37262476a4906db2979178f3ad1ebad911c6e0252a6964b4330956",
"md5": "3eb7e89237abf3a2db2b9d19de324203",
"sha256": "b23c4e8671ff559cb95fbb4a592a4eece6a7b6899bbedbb724741dd3d320f324"
},
"downloads": -1,
"filename": "pyproj_inspector-0.1.2.tar.gz",
"has_sig": false,
"md5_digest": "3eb7e89237abf3a2db2b9d19de324203",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 19118,
"upload_time": "2025-09-11T03:25:21",
"upload_time_iso_8601": "2025-09-11T03:25:21.419427Z",
"url": "https://files.pythonhosted.org/packages/5b/7a/0903fa37262476a4906db2979178f3ad1ebad911c6e0252a6964b4330956/pyproj_inspector-0.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-11 03:25:21",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "pyproj-inspector"
}