Skip to content
This repository has been archived by the owner on Jan 12, 2021. It is now read-only.

Commit

Permalink
Merge branch 'audit-command'
Browse files Browse the repository at this point in the history
  • Loading branch information
orsinium committed Apr 10, 2019
2 parents a007097 + 75609bb commit ce57ce8
Show file tree
Hide file tree
Showing 116 changed files with 714 additions and 246 deletions.
6 changes: 3 additions & 3 deletions dephell/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# app
from .controllers import * # noQA
from .converters import * # noQA
from .models import * # noQA
from .__version__ import * # noQA
from .controllers import * # noQA
from .converters import * # noQA
from .models import * # noQA
7 changes: 6 additions & 1 deletion dephell/actions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""Actions are functions that used only in commands
"""

# app
from ._autocomplete import make_bash_autocomplete, make_zsh_autocomplete
from ._converting import attach_deps
from ._downloads import get_total_downloads, get_downloads_by_category
from ._downloads import get_downloads_by_category, get_total_downloads
from ._editorconfig import make_editorconfig
from ._entrypoints import get_entrypoints
from ._json import make_json
from ._package import get_package, get_packages, get_resolver
from ._python import get_python, get_python_env
from ._roman import arabic2roman, roman2arabic
from ._venv import get_venv
Expand All @@ -21,8 +23,11 @@
'bump_version',
'get_downloads_by_category',
'get_entrypoints',
'get_package',
'get_packages',
'get_python_env',
'get_python',
'get_resolver',
'get_total_downloads',
'get_venv',
'get_version_from_file',
Expand Down
2 changes: 2 additions & 0 deletions dephell/actions/_autocomplete.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# built-in
from collections import defaultdict

# external
from jinja2 import Environment, PackageLoader


Expand Down
2 changes: 2 additions & 0 deletions dephell/actions/_converting.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# built-in
from logging import getLogger

# app
from ..config import Config
from ..converters import CONVERTERS

Expand Down
3 changes: 2 additions & 1 deletion dephell/actions/_downloads.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
from collections import defaultdict
from datetime import date, timedelta
from itertools import zip_longest
from typing import Iterable, Iterator, Dict, List
from typing import Dict, Iterable, Iterator, List

# external
import attr
import requests

Expand Down
2 changes: 1 addition & 1 deletion dephell/actions/_editorconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def __str__(self) -> str:
def make_editorconfig(path: Path) -> str:
matched = []
non_matched = []
for i, rule in enumerate(RULES):
for rule in RULES:
if rule.match(path):
matched.append(rule)
else:
Expand Down
6 changes: 5 additions & 1 deletion dephell/actions/_entrypoints.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# built-in
from logging import getLogger
from typing import Tuple

# project
from dephell_venvs import VEnv

# app
from ..converters import EggInfoConverter
from ..models import EntryPoint
from dephell_venvs import VEnv


logger = getLogger('dephell.actions')
Expand Down
11 changes: 11 additions & 0 deletions dephell/actions/_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,19 @@ def _each(value):
return new_value


def _flatten(value):
if not isinstance(value, (list, tuple)):
return [value]
new_value = []
for element in value:
new_value.extend(_flatten(element))
return new_value


FILTERS = {
'each()': _each,
'first()': lambda v: v[0],
'flatten()': _flatten,
'last()': lambda v: v[-1],
'len()': lambda v: len(v),
'max()': lambda v: max(v),
Expand All @@ -34,6 +44,7 @@ def _each(value):

# aliases
'#': _each,
'flat()': _flatten,
'latest()': lambda v: v[-1],
'length()': lambda v: len(v),
'reversed()': lambda v: v[::-1],
Expand Down
20 changes: 20 additions & 0 deletions dephell/actions/_package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# built-in
from typing import List

# app
from ..controllers import Resolver
from ..converters import PIPConverter
from ..models import Dependency


def get_packages(req: str) -> List[Dependency]:
root = PIPConverter(lock=False).loads(req)
return root.dependencies


def get_package(req: str) -> Dependency:
return get_packages(req=req)[0]


def get_resolver(req: str) -> Resolver:
return PIPConverter(lock=False).loads_resolver(req)
2 changes: 1 addition & 1 deletion dephell/actions/_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from dephell_pythons import Python, Pythons

# app
from ..converters import CONVERTERS
from ..config import Config
from ..converters import CONVERTERS
from ._venv import get_venv


Expand Down
1 change: 0 additions & 1 deletion dephell/actions/_roman.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

NUMBERS = [
(1000, 'M'),
(900, 'CM'),
Expand Down
5 changes: 4 additions & 1 deletion dephell/actions/_venv.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# built-in
from pathlib import Path

# project
from dephell_venvs import VEnv, VEnvs

# app
from ..config import Config
from dephell_venvs import VEnvs, VEnv


def get_venv(config: Config) -> VEnv:
Expand Down
8 changes: 6 additions & 2 deletions dephell/actions/_version.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# built-in
import re
from datetime import date
from pathlib import Path
from typing import Iterator, Union, Optional
from typing import Iterator, Optional, Union

# external
from dephell_discover import Root
from packaging.version import Version, VERSION_PATTERN
from packaging.version import VERSION_PATTERN, Version

# app
from .. import constants
from ._roman import arabic2roman, roman2arabic


FILE_NAMES = (
'__init__.py',
'__version__.py',
Expand Down
3 changes: 3 additions & 0 deletions dephell/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# app
from .autocomplete import AutocompleteCommand
from .deps_audit import DepsAuditCommand
from .deps_convert import DepsConvertCommand
from .deps_install import DepsInstallCommand
from .deps_licenses import DepsLicensesCommand
Expand Down Expand Up @@ -32,6 +33,7 @@
__all__ = [
'AutocompleteCommand',
'commands',
'DepsAuditCommand',
'DepsConvertCommand',
'DepsInstallCommand',
'DepsLicensesCommand',
Expand Down Expand Up @@ -64,6 +66,7 @@

commands = {
'autocomplete': AutocompleteCommand,
'deps audit': DepsAuditCommand,
'deps convert': DepsConvertCommand,
'deps install': DepsInstallCommand,
'deps licenses': DepsLicensesCommand,
Expand Down
3 changes: 2 additions & 1 deletion dephell/commands/autocomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from argparse import ArgumentParser
from pathlib import Path

# external
from appdirs import user_data_dir
from dephell_shells import Shells

Expand All @@ -18,7 +19,7 @@ class AutocompleteCommand(BaseCommand):
"""

@classmethod
def get_parser(cls):
def get_parser(cls) -> ArgumentParser:
parser = ArgumentParser(
prog='dephell autocomplete',
description=cls.__doc__,
Expand Down
8 changes: 6 additions & 2 deletions dephell/commands/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# built-in
import os.path
from argparse import ArgumentParser
from logging import getLogger

# app
from ..config import config, Config
from ..config import Config, config


class BaseCommand:
Expand All @@ -19,7 +20,7 @@ def __init__(self, argv, config: Config = None):
self.config = config

@classmethod
def get_parser(cls):
def get_parser(cls) -> ArgumentParser:
raise NotImplementedError

@classmethod
Expand All @@ -43,3 +44,6 @@ def validate(self) -> bool:
self.logger.error('invalid config')
print(self.config.format_errors())
return is_valid

def __call__(self) -> bool:
raise NotImplementedError
97 changes: 97 additions & 0 deletions dephell/commands/deps_audit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# built-in
from argparse import REMAINDER, ArgumentParser

# app
from ..actions import get_packages, get_python_env, make_json
from ..config import builders
from ..controllers import Safety, Snyk
from ..converters import CONVERTERS, InstalledConverter
from .base import BaseCommand


class DepsAuditCommand(BaseCommand):
"""Show known vulnerabilities for project dependencies.
https://dephell.readthedocs.io/en/latest/cmd-deps-audit.html
"""
@classmethod
def get_parser(cls) -> ArgumentParser:
parser = ArgumentParser(
prog='dephell deps audit',
description=cls.__doc__,
)
builders.build_config(parser)
builders.build_to(parser)
builders.build_output(parser)
builders.build_api(parser)
builders.build_other(parser)
parser.add_argument('name', nargs=REMAINDER, help='package name and version')
return parser

def __call__(self) -> bool:
packages = None

# get packages from CLI
if self.args.name:
packages = get_packages(req=' '.join(self.args.name))
for dep in packages:
if not str(dep.constraint).startswith('=='):
self.logger.error('please, specify version for package', extra=dict(
package=dep.name,
constraint=str(dep.constraint),
))
return False

# get packages from lockfile
if packages is None:
loader_config = self.config.get('to') or self.config.get('from')
if loader_config is not None:
loader = CONVERTERS[loader_config['format']]
if loader.lock:
self.logger.info('get dependencies from lockfile', extra=dict(
format=loader_config['format'],
path=loader_config['path'],
))
root = loader.load(path=loader_config['path'])
packages = root.dependencies

# get installed packages
if packages is None:
# get executable
python = get_python_env(config=self.config)
self.logger.debug('choosen python', extra=dict(path=str(python.path)))
root = InstalledConverter().load(paths=python.lib_paths)
packages = root.dependencies

safety = Safety()
snyk = Snyk()

data = []
for dep in packages:
versions = str(dep.constraint).replace('=', '').split(' || ')
for version in versions:
vulns = safety.get(name=dep.name, version=version)
vulns += snyk.get(name=dep.name, version=version)
if not vulns:
continue
releases = dep.repo.get_releases(dep)
for vuln in vulns:
data.append(dict(
# local info
name=dep.name,
current=version,
# pypi info
latest=str(releases[0].version),
updated=str(releases[0].time.date()),
# vuln info
description=vuln.description,
links=vuln.links,
vulnerable=str(vuln.specifier),
))

if data:
print(make_json(data=data, key=self.config.get('filter')))
return False

self.logger.info('dependencies has no known vulnerabilities (yet)')
return True
4 changes: 2 additions & 2 deletions dephell/commands/deps_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class DepsConvertCommand(BaseCommand):
https://dephell.readthedocs.io/en/latest/cmd-deps-convert.html
"""
@classmethod
def get_parser(cls):
def get_parser(cls) -> ArgumentParser:
parser = ArgumentParser(
prog='dephell deps convert',
description=cls.__doc__,
Expand All @@ -30,7 +30,7 @@ def get_parser(cls):
builders.build_other(parser)
return parser

def __call__(self):
def __call__(self) -> bool:
loader = CONVERTERS[self.config['from']['format']]
dumper = CONVERTERS[self.config['to']['format']]

Expand Down
4 changes: 2 additions & 2 deletions dephell/commands/deps_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from argparse import ArgumentParser

# app
from ..actions import get_python_env, attach_deps
from ..actions import attach_deps, get_python_env
from ..config import builders
from ..controllers import analize_conflict
from ..converters import CONVERTERS, InstalledConverter
Expand All @@ -17,7 +17,7 @@ class DepsInstallCommand(BaseCommand):
https://dephell.readthedocs.io/en/latest/cmd-deps-install.html
"""
@classmethod
def get_parser(cls):
def get_parser(cls) -> ArgumentParser:
parser = ArgumentParser(
prog='dephell deps install',
description=cls.__doc__,
Expand Down

0 comments on commit ce57ce8

Please sign in to comment.