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

Commit

Permalink
Merge pull request #349 from dephell/lazy
Browse files Browse the repository at this point in the history
Lazy imports
  • Loading branch information
orsinium committed Dec 27, 2019
2 parents 2ddf473 + 2d035ca commit 69758ee
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 133 deletions.
35 changes: 26 additions & 9 deletions dephell/actions/_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,15 @@
from functools import reduce
from typing import Optional

# external
from flatdict import FlatDict
from pygments import formatters, highlight, lexers
from tabulate import tabulate
# app
from ..imports import lazy_import


flatdict = lazy_import('flatdict')
pygments = lazy_import('pygments')
pygments_lexers = lazy_import('pygments.lexers')
pygments_formatters = lazy_import('pygments.formatters')
tabulate = lazy_import('tabulate')


def _each(value):
Expand Down Expand Up @@ -96,23 +101,35 @@ def _beautify(data, *, colors: bool, table: bool) -> str:
if table:
# one dict
if isinstance(data, dict):
data = FlatDict(data, delimiter='.').items()
return tabulate(data, headers=('key', 'value'), tablefmt='fancy_grid')
data = flatdict.FlatDict(data, delimiter='.').items()
return tabulate.tabulate(
data,
headers=('key', 'value'),
tablefmt='fancy_grid',
)
# list of dicts
if isinstance(data, list) and data and isinstance(data[0], dict):
table = []
for row in data:
row = FlatDict(row, delimiter='.')
row = flatdict.FlatDict(row, delimiter='.')
keys = tuple(row)
row = [v for _, v in sorted(row.items())]
table.append(row)
return tabulate(table, headers=keys, tablefmt='fancy_grid')
return tabulate.tabulate(
table,
headers=keys,
tablefmt='fancy_grid',
)

json_params = dict(indent=2, sort_keys=True, ensure_ascii=False)
dumped = json.dumps(data, **json_params)
if not colors:
return dumped
return highlight(dumped, lexers.JsonLexer(), formatters.TerminalFormatter())
return pygments.highlight(
code=dumped,
lexer=pygments_lexers.JsonLexer(),
formatter=pygments_formatters.TerminalFormatter(),
)


def make_json(data, key: str = None, sep: Optional[str] = '-',
Expand Down
64 changes: 34 additions & 30 deletions dephell/actions/_transform.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
# built-in
from typing import TYPE_CHECKING

# external
from fissix.fixer_util import Dot, Name, syms
from fissix.pytree import Node
# app
from ..imports import lazy_import


bowler_helpers = lazy_import('bowler.helpers')
fissix_pytree = lazy_import('fissix.pytree')
fixer_util = lazy_import('fissix.fixer_util')


if TYPE_CHECKING:
import bowler.helpers as bowler_helpers
import fissix.pytree as fissix_pytree
from bowler import LN, Capture, Filename, Query

try:
from bowler.helpers import dotted_parts, power_parts, quoted_parts
except ImportError:
pass
from fissix import fixer_util


modifiers = []
Expand All @@ -25,8 +28,8 @@ def _register(modifier):
def transform_imports(query: 'Query', old_name: str, new_name: str) -> 'Query':
params = dict(
name=old_name,
dotted_name=' '.join(quoted_parts(old_name)),
power_name=' '.join(power_parts(old_name)),
dotted_name=' '.join(bowler_helpers.quoted_parts(old_name)),
power_name=' '.join(bowler_helpers.power_parts(old_name)),
)
for modifier_class in modifiers:
modifier = modifier_class(old_name=old_name, new_name=new_name)
Expand Down Expand Up @@ -57,15 +60,15 @@ def __init__(self, old_name, new_name):

def __call__(self, node: 'LN', capture: 'Capture', filename: 'Filename') -> None:
old_node = capture['module_name']
new_node = Node(
type=syms.dotted_as_name,
new_node = fissix_pytree.Node(
type=fixer_util.syms.dotted_as_name,
children=[
build_new_name_node(
old_node=old_node,
new_name=self.new_name,
attach=False,
),
Name('as', prefix=' '),
fixer_util.Name('as', prefix=' '),
old_node.clone(),
],
)
Expand Down Expand Up @@ -200,7 +203,7 @@ def __init__(self, old_name, new_name):
self.new_name = new_name

def __call__(self, node: 'LN', capture: 'Capture', filename: 'Filename') -> None:
if node.type == syms.power:
if node.type == fixer_util.syms.power:
self._modify_power(node)
else:
self._modify_import(capture)
Expand All @@ -218,17 +221,18 @@ def _modify_power(self, node):
prefix = node.children[0].prefix

# remove old prefix
parts = dotted_parts(self.old_name)
parts = bowler_helpers.dotted_parts(self.old_name)
for _ in range((len(parts) + 1) // 2):
node.children.pop(0)

# add new prefix
head = Name(self.new_name.split('.', maxsplit=1)[0], prefix=prefix)
name = self.new_name.split('.', maxsplit=1)[0]
head = fixer_util.Name(name, prefix=prefix)
children = []
for part in dotted_parts(self.new_name)[2::2]:
children.append(Node(
type=syms.trailer,
children=[Dot(), Name(part)],
for part in bowler_helpers.dotted_parts(self.new_name)[2::2]:
children.append(fissix_pytree.Node(
type=fixer_util.syms.trailer,
children=[fixer_util.Dot(), fixer_util.Name(part)],
))
node.children = [head] + children + node.children

Expand All @@ -237,25 +241,25 @@ def build_new_name_node(*, old_node, attach: bool, new_name: str, old_name: str
# build new node from new_name
if '.' in new_name:
children = []
for part in dotted_parts(new_name):
for part in bowler_helpers.dotted_parts(new_name):
if part == '.':
children.append(Dot())
children.append(fixer_util.Dot())
else:
children.append(Name(part))
children.append(fixer_util.Name(part))
else:
children = [Name(new_name)]
children = [fixer_util.Name(new_name)]

# attach to the new node subimports from the old module
if attach and type(old_node) is Node:
original_name_size = len(dotted_parts(old_name))
if attach and type(old_node) is fissix_pytree.Node:
original_name_size = len(bowler_helpers.dotted_parts(old_name))
for part in old_node.children[original_name_size:]:
if part.value == '.':
children.append(Dot())
children.append(fixer_util.Dot())
else:
children.append(Name(part.value))
children.append(fixer_util.Name(part.value))

return Node(
type=syms.dotted_name,
return fissix_pytree.Node(
type=fixer_util.syms.dotted_name,
children=children,
prefix=old_node.prefix,
)
14 changes: 8 additions & 6 deletions dephell/commands/vendor_import.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
# built-in
from argparse import ArgumentParser
from pathlib import Path
from typing import TYPE_CHECKING

# app
from ..actions import transform_imports
from ..config import builders
from ..imports import lazy_import
from .base import BaseCommand


try:
from bowler import Query
except ImportError:
Query = None
bowler = lazy_import('bowler')

if TYPE_CHECKING:
import bowler


class VendorImportCommand(BaseCommand):
Expand All @@ -28,7 +30,7 @@ def build_parser(parser) -> ArgumentParser:
return parser

def __call__(self) -> bool:
if Query is None:
if bowler.Query is None:
raise RuntimeError('vendorization is unsupported on Windows')
resolver = self._get_locked()
if resolver is None:
Expand All @@ -41,7 +43,7 @@ def __call__(self) -> bool:

def _patch_imports(self, resolver, output_path: Path) -> int:
# select modules to patch imports
query = Query()
query = bowler.Query()
query.paths = []
for package in resolver.graph.metainfo.package.packages:
for module_path in package:
Expand Down
9 changes: 6 additions & 3 deletions dephell/config/app_dirs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
from functools import lru_cache
from pathlib import Path

# external
from appdirs import user_data_dir
# app
from ..imports import lazy_import


appdirs = lazy_import('appdirs')


@lru_cache(maxsize=2)
Expand All @@ -25,7 +28,7 @@ def get_data_dir(app: str = 'dephell') -> Path:
if path.exists():
return path / app

return Path(user_data_dir(app))
return Path(appdirs.user_data_dir(app))


@lru_cache(maxsize=2)
Expand Down
20 changes: 11 additions & 9 deletions dephell/controllers/_docker.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
# built-in
from logging import getLogger
from pathlib import Path
from typing import List, Optional
from typing import TYPE_CHECKING, List, Optional

# external
import attr
import docker
from dephell_venvs import VEnvs

# app
from ..cached_property import cached_property
from ..imports import lazy_import
from ..networking import requests_session


try:
docker = lazy_import('docker')
dockerpty = lazy_import('dockerpty')

if TYPE_CHECKING:
import docker
import dockerpty
except ImportError:
dockerpty = None


DOCKER_PREFIX = 'dephell-'


class DockerContainers:
@cached_property
def client(self) -> docker.DockerClient:
def client(self) -> 'docker.DockerClient':
return docker.from_env()

def list(self):
Expand Down Expand Up @@ -81,18 +83,18 @@ def tags(self):
return sorted(tags, key=lambda tag: tags[tag], reverse=True)

@cached_property
def client(self) -> docker.DockerClient:
def client(self) -> 'docker.DockerClient':
return docker.from_env()

@cached_property
def container(self) -> Optional[docker.models.containers.Container]:
def container(self) -> Optional['docker.models.containers.Container']:
try:
return self.client.containers.get(self.container_name)
except docker.errors.NotFound:
return None

@cached_property
def network(self) -> Optional[docker.models.networks.Network]:
def network(self) -> Optional['docker.models.networks.Network']:
return self.client.networks.get(self.network_name)

# public methods
Expand Down
21 changes: 14 additions & 7 deletions dephell/controllers/_graph.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
# built-in
from collections import ChainMap
from logging import getLogger
from typing import Optional
from typing import TYPE_CHECKING, Optional

# app
from ..imports import lazy_import
from ..models.dependency import Dependency
from ..models.root import RootDependency


graphviz = lazy_import('graphviz')
graphviz_backend = lazy_import('graphviz.backend')


if TYPE_CHECKING:
import graphviz
import graphviz.backend as graphviz_backend


logger = getLogger(__name__)


Expand Down Expand Up @@ -184,10 +194,7 @@ def get_parents(self, *deps, avoid: Optional[list] = None) -> dict:
return parents

def draw(self, path: str = '.dephell_report', suffix: str = '') -> None:
from graphviz import Digraph
from graphviz.backend import ExecutableNotFound

dot = Digraph(
dot = graphviz.Digraph(
name=self._roots[0].name + suffix,
directory=path,
format='png',
Expand Down Expand Up @@ -217,8 +224,8 @@ def draw(self, path: str = '.dephell_report', suffix: str = '') -> None:
# save files
try:
dot.render()
except ExecutableNotFound as e:
raise ImportError('GraphViz is not installed yet.') from e
except graphviz_backend.ExecutableNotFound as e:
raise ImportError('GraphViz binary is not installed yet.') from e

# properties

Expand Down

0 comments on commit 69758ee

Please sign in to comment.