Metadata-Version: 2.4
Name: unbox
Version: 0.1.12
Summary: Finding imports in code
Project-URL: Homepage, https://github.com/i2mint/unbox
Project-URL: Documentation, https://i2mint.github.io/unbox/
Author: Thor Whalen
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: dependencies,findimports,imports,packaging,requirements,static-analysis
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: config2py
Requires-Dist: dol>=0.3.49
Requires-Dist: findimports>=2.4
Requires-Dist: importlib-resources
Requires-Dist: py2store
Requires-Dist: xdol
Provides-Extra: dev
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: sphinx-rtd-theme>=1.0; extra == 'docs'
Requires-Dist: sphinx>=6.0; extra == 'docs'
Description-Content-Type: text/markdown

# unbox
Finding imports in code

To install:	```pip install unbox```

# What's here

Lots of little goodies to help you analyze the imports of your, or others' code. 

## Getting a list of missing dependencies

```python
>>> from unbox import print_missing_names
>>> import some_module  # doctest: +SKIP
>>> print_missing_names(some_module)  # doctest: +SKIP
SoundFile
i2
librosa
pyttsx3
slink
```

These are the names a package imports but doesn't declare. The declared names are
read from the package's `pyproject.toml` (PEP 621 `[project] dependencies`), falling
back to `setup.cfg` (`[options] install_requires`) for legacy projects:

```python
>>> import unbox
>>> unbox.find_install_names(unbox)  # doctest: +SKIP
['findimports', 'dol>=0.3.49', 'importlib_resources', 'config2py', 'py2store', 'xdol']
```

Use `unbox.module_requirements_according_to_pyproject(pkg, extras=True)` if you also
want the `[project.optional-dependencies]` groups.

## Seeing what the dependencies of a package are, before installing it

Simply get a list of dependencies for a package from PyPI.

```python
>>> import unbox
>>> unbox.dependencies_from_pypi('pandas')
['numpy', 'numpy', 'python-dateutil', 'pytz', 'tzdata']
```

But you have control over the requirements that are returned, 
and how they are returned:

```python
>>> it = unbox.dependencies_from_pypi(
...     'pandas',
...     requirement_filter=lambda x: True,  # don't filter any requirements
...     requirement_trans=lambda x: x,  # as is
...     egress = lambda x: x  # just get the iterator as is
... )
>>> next(it)
'numpy>=1.22.4; python_version < "3.11"'
>>> list(it)[-1]
'zstandard>=0.17.0; extra == "all"'
```


## A dict-like interface

The base of `unbox` is just the `dol` interface to `findimports`, which then allows us to offer a bunch of functionalities easily. 

Say you wanted to know what dol was made of. 
The dol way of doing this is to make a `Mapping` (i.e. a key-value dict-like interface), 
and then do what you do with dicts...

```python
>>> import dol
>>> import unbox
>>> s = unbox.ModuleNamesImportedByModule(dol)  # make a store containing the modules of the `dol` package
>>> # Now wee how you can do things you do with dicts
>>> len(s)
15
>>> list(s)
['dol.__init__',
 'dol.appendable',
 'dol.base',
 'dol.caching',
 'dol.core',
 'dol.dig',
 'dol.errors',
 'dol.filesys',
 'dol.mixins',
 'dol.naming',
 'dol.paths',
 'dol.signatures',
 'dol.sources',
 'dol.trans',
 'dol.util']
>>> 'dol.appendable' in s
>>> # The values of `s` are sets of modules imported by a module.
>>> s['dol.appendable']  # what does dol.appendable import?
{'collections.abc', 'dol.trans', 'time', 'types', 'typing'}
```

Check out `ModulesImportedByModule` also, which gives you a `Mapping` with module objects 
as keys, and `findimports.ImportInfo` instances as values.

## imports_for

As an example of what you can do with this set up, have a look at `imports_for`. 
Or don't have a look; just use it, since it's quite useful.

```python
from unbox import imports_for
import wave

assert {"collections", "struct", "sys"}.issubset(imports_for(wave))
```

Note that we only check a subset here: what a stdlib module imports changes
between python versions (py3.10's `wave` imports `audioop` and `chunk`, both
removed in 3.13, while py3.12's imports `uuid`).

At it's base, imports_for gives you a generator of import names. 
With the `post` argument (defaulted to `set`) you can specify a callable that can produce the output 
you want; whether you want to filter the items, count them, order them, etc.

We curried a few common ones for you, for your convenience:

```python
from unbox import imports_for

imports_for.counter  # imported names and their counts
imports_for.most_common  # imported names and their counts, ordered by most common
imports_for.first_level  # set for imported first level names (e.g. 'os' instead of 'os.path.etc.)
imports_for.first_level_count  # count of imported first level names (e.g. 'os' instead of 'os.path.etc.)
imports_for.third_party  # imported (first level) names that are not builtin names (most probably third party packages)"
```

## Collections of python names

Check out the contents of these collections:

```python
from unbox import (
    builtin_module_names,
    scanned_standard_lib_names,
    all_accessible_modules,
    all_accessible_pkg_names,
    all_accessible_non_pkg_module_names,
    builtin_obj_names,
    python_names,
)
```

For example, `builtin_module_names` will be a set of names that are 
[documented](`https://docs.python.org/3.8/library/`) **and** importable on your system.

The `scanned_standard_lib_names` set is similar, but the names are obtained by scanning 
the local standard library file names -- so include things like easter eggs (`this`, `antigravity`).

`all_accessible_modules` will be the list of all modules accessible in your python path.

And so on...

