Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: pre-commit

on:
pull_request:
push:
branches: [main]

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
- uses: pre-commit/action@v3.0.0
3 changes: 1 addition & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
uses: mamba-org/provision-with-micromamba@v15
with:
environment-file: false

- name: Python ${{ matrix.python-version }}
run: |
micromamba create --name TEST python=${{ matrix.python-version }} pip --file requirements-dev.txt --channel conda-forge
Expand All @@ -47,4 +47,3 @@ jobs:
micromamba activate TEST
coverage report -m
codecov

11 changes: 11 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black
2 changes: 0 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,5 +117,3 @@ v2.4.0

* Eric Dill
* Christopher J. Wright


5 changes: 3 additions & 2 deletions depfinder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@

from .inspection import parse_file

from .main import (iterate_over_library, notebook_path_to_dependencies)
from .main import iterate_over_library, notebook_path_to_dependencies

import logging
logger = logging.getLogger('depfinder')

logger = logging.getLogger("depfinder")
146 changes: 84 additions & 62 deletions depfinder/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@

from . import main
from .inspection import parse_file
from .main import (simple_import_search, notebook_path_to_dependencies,
sanitize_deps)
from .main import simple_import_search, notebook_path_to_dependencies, sanitize_deps

logger = logging.getLogger('depfinder')
logger = logging.getLogger("depfinder")


class InvalidSelection(RuntimeError):
Expand All @@ -68,81 +67,92 @@ def _init_parser():
nargs="?",
)
p.add_argument(
'-y',
'--yaml',
action='store_true',
"-y",
"--yaml",
action="store_true",
default=False,
help=("Output in syntactically valid yaml when true. Defaults to "
"%(default)s"))
help=(
"Output in syntactically valid yaml when true. Defaults to " "%(default)s"
),
)
p.add_argument(
'-V',
'--version',
action='store_true',
"-V",
"--version",
action="store_true",
default=False,
help="Print out the version of depfinder and exit"
help="Print out the version of depfinder and exit",
)
p.add_argument(
'--no-remap',
action='store_true',
"--no-remap",
action="store_true",
default=False,
help=("Do not remap the names of the imported libraries to their "
"proper conda name")
help=(
"Do not remap the names of the imported libraries to their "
"proper conda name"
),
)
p.add_argument(
'-v',
'--verbose',
action='store_true',
"-v",
"--verbose",
action="store_true",
default=False,
help="Enable debug level logging info from depfinder"
help="Enable debug level logging info from depfinder",
)
p.add_argument(
'-q',
'--quiet',
action='store_true',
"-q",
"--quiet",
action="store_true",
default=False,
help="Turn off all logging from depfinder"
help="Turn off all logging from depfinder",
)
p.add_argument(
'-k', '--key',
"-k",
"--key",
action="append",
default=[],
help=("Select some or all of the output keys. Valid options are "
"'required', 'optional', 'builtin', 'relative', 'all'. Defaults "
"to 'all'")
help=(
"Select some or all of the output keys. Valid options are "
"'required', 'optional', 'builtin', 'relative', 'all'. Defaults "
"to 'all'"
),
)
p.add_argument(
'--conda',
"--conda",
action="store_true",
default=False,
help=("Format output so it can be passed as an argument to conda "
"install or conda create")
help=(
"Format output so it can be passed as an argument to conda "
"install or conda create"
),
)
p.add_argument(
'--pdb',
"--pdb",
action="store_true",
help="Enable PDB debugging on exception",
default=False,
)
p.add_argument(
'--ignore',
default='',
help="Comma separated list of file patterns not to inspect"
"--ignore",
default="",
help="Comma separated list of file patterns not to inspect",
)
p.add_argument(
'--strict',
"--strict",
default=False,
action="store_true",
help=("Immediately raise an Exception if any files fail to parse. Defaults to off.")
help=(
"Immediately raise an Exception if any files fail to parse. Defaults to off."
),
)
p.add_argument(
'--custom-namespaces',
default='',
"--custom-namespaces",
default="",
help=(
"A comma-separated list of custom namespace packages. "
"Listing namespaces here will enable depfinder to properly report "
"dependencies when a namespace is in use (e.g., depfinder will report "
"both foo.bar and foo.baz instead of foo when run with --custom-namespaces=foo)."
)
),
)
return p

Expand All @@ -151,14 +161,17 @@ def cli():
p = _init_parser()
args = p.parse_args()
if args.verbose and args.quiet:
msg = ("You have enabled both verbose mode (--verbose or -v) and "
"quiet mode (-q or --quiet). Please pick one. Exiting...")
msg = (
"You have enabled both verbose mode (--verbose or -v) and "
"quiet mode (-q or --quiet). Please pick one. Exiting..."
)
raise InvalidSelection(msg)

if args.pdb:
# set the pdb_hook as the except hook for all exceptions
def pdb_hook(exctype, value, traceback):
pdb.post_mortem(traceback)

sys.excepthook = pdb_hook

cs = args.custom_namespaces.split(",")
Expand All @@ -173,7 +186,7 @@ def pdb_hook(exctype, value, traceback):
loglevel = logging.DEBUG
stream_handler = logging.StreamHandler()
stream_handler.setLevel(loglevel)
f = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
f = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
formatter = logging.Formatter(f)
stream_handler.setFormatter(formatter)
logger.setLevel(loglevel)
Expand All @@ -183,6 +196,7 @@ def pdb_hook(exctype, value, traceback):
# handle the case where the user just cares about the version. Print
# version and exit
from . import __version__

print(__version__)
return 0

Expand All @@ -194,7 +208,7 @@ def pdb_hook(exctype, value, traceback):
keys = args.key
if keys == []:
keys = None
logger.debug('keys: %s', keys)
logger.debug("keys: %s", keys)

def dump_deps(deps, keys):
"""
Expand All @@ -211,28 +225,35 @@ def dump_deps(deps, keys):
if args.yaml:
print(yaml.dump(deps, default_flow_style=False))
elif args.conda:
list_of_deps = [item for sublist in itertools.chain(deps.values())
for item in sublist]
print(' '.join(list_of_deps))
list_of_deps = [
item for sublist in itertools.chain(deps.values()) for item in sublist
]
print(" ".join(list_of_deps))
else:
pprint(deps)

if os.path.isdir(file_or_dir):
logger.debug("Treating {} as a directory and recursively searching "
"it for python files".format(file_or_dir))
logger.debug(
"Treating {} as a directory and recursively searching "
"it for python files".format(file_or_dir)
)
# directories are a little easier from the purpose of the API call.
# print the dependencies to the console and then exit
ignore = args.ignore.split(',')
ignore = args.ignore.split(",")
deps = simple_import_search(
file_or_dir, remap=not args.no_remap,
ignore=ignore, custom_namespaces=cs,
file_or_dir,
remap=not args.no_remap,
ignore=ignore,
custom_namespaces=cs,
)
dump_deps(deps, keys)
return 0
elif os.path.isfile(file_or_dir):
if file_or_dir.endswith('ipynb'):
logger.debug("Treating {} as a jupyter notebook and searching "
"all of its code cells".format(file_or_dir))
if file_or_dir.endswith("ipynb"):
logger.debug(
"Treating {} as a jupyter notebook and searching "
"all of its code cells".format(file_or_dir)
)
deps = notebook_path_to_dependencies(
file_or_dir,
remap=not args.no_remap,
Expand All @@ -242,9 +263,8 @@ def dump_deps(deps, keys):
# print the dependencies to the console and then exit
dump_deps(sanitized, keys)
return 0
elif file_or_dir.endswith('.py'):
logger.debug("Treating {} as a single python file"
"".format(file_or_dir))
elif file_or_dir.endswith(".py"):
logger.debug("Treating {} as a single python file" "".format(file_or_dir))
mod, path, import_finder = parse_file(file_or_dir, custom_namespaces=cs)
mods = defaultdict(set)
for k, v in import_finder.describe().items():
Expand All @@ -258,8 +278,10 @@ def dump_deps(deps, keys):
else:
# Any file with a suffix that is not ".ipynb" or ".py" will not
# be parsed correctly
msg = ("depfinder is only configured to work with jupyter "
"notebooks and python source code files. It is anticipated "
"that the file {} will not work with depfinder"
"".format(file_or_dir))
msg = (
"depfinder is only configured to work with jupyter "
"notebooks and python source code files. It is anticipated "
"that the file {} will not work with depfinder"
"".format(file_or_dir)
)
raise RuntimeError(msg)
Loading