| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
0 parents commit a341360
16 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,13 @@ | |||
| 1 | + .idea/ | ||
| 2 | + env/ | ||
| 3 | + *.py[cod] | ||
| 4 | + *.egg-info/ | ||
| 5 | + build/ | ||
| 6 | + dist/ | ||
| 7 | + .cache/ | ||
| 8 | + .mypy_cache/ | ||
| 9 | + test.py | ||
| 10 | + .coverage | ||
| 11 | + htmlcov/ | ||
| 12 | + benchmarks/*.json | ||
| 13 | + docs/_build/ | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1 @@ | |||
| 1 | + schedule: 'every week' | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,47 @@ | |||
| 1 | + language: python | ||
| 2 | + | ||
| 3 | + cache: pip | ||
| 4 | + | ||
| 5 | + python: | ||
| 6 | + - '3.5' | ||
| 7 | + - '3.6' | ||
| 8 | + - 'nightly' # currently 3.7 | ||
| 9 | + | ||
| 10 | + matrix: | ||
| 11 | + allow_failures: | ||
| 12 | + - python: 'nightly' | ||
| 13 | + | ||
| 14 | + install: | ||
| 15 | + - make install | ||
| 16 | + - pip freeze | ||
| 17 | + | ||
| 18 | + script: | ||
| 19 | + - make lint | ||
| 20 | + | ||
| 21 | + - make test | ||
| 22 | + #- make docs-lint | ||
| 23 | + #- make docs | ||
| 24 | + - ./tests/check_tag.py | ||
| 25 | + | ||
| 26 | + after_success: | ||
| 27 | + - ls -lha | ||
| 28 | + - bash <(curl -s https://codecov.io/bash) | ||
| 29 | + | ||
| 30 | + env: | ||
| 31 | + - secure: "vpTd8bkwPBP0CV3EJBAwSMNMnNK3m/71dvTvBd1T4YGuefyJvYhtA7wauA5xRL9jpK2mu5QR5eo0owTUJhKi4DjpafMMd1bc4PnXlrdZFzkn3VsGmlKt74D/aJgiuiNyhd/Qvq4OxMHrMhf4f6lKWoMM1vh6yT0yp3+51SexSh2Me0Q+npxbjXwoxX5XUHRcoSLtFk4GbYI88a2I+08XWI6v+Awo/giQ5QurUJhjAklbosrrQVr1FCOkU0em5jeyZvEbZSLmaMtbX1JlRdKoJm6WMU+y9I7zj35w6ue/vgfcLz7b/HDZrBx7/L9g1LxRo80briueX/IbHvN7DOVFKvaXVmnEa6lIDdCeOLOyESpIbmjqmDKi8JeexdPNxKq4Tvo2VEA9dL2w2aw+aALNtU2OF5iEMfPTUQyosu/CNu2PKtiuZkSOdvpYbSy1WUNHJRvomdR4Olzg8ZIScNsxU3IIPdrlG/LUA8auXcE9juFeZfD6D2hQZATqWeEe/C2J7amNSD+mLLaTf6nMQw8oNtKYOvYK17M7xyvi7HXDy711Bi18U3x6Ye0xGx8CDbFwl0ICNzIk9rrSAh9hEHTvfdUUkk35pxifvO0Hrh4SArCA20ozcH/hHWBhyqGdxoIQ6KoDgNbIFIGQ6/vugxL/pt8z1sJwPfJnq8tRDAyWZvE=" | ||
| 32 | + | ||
| 33 | + deploy: | ||
| 34 | + - provider: pypi | ||
| 35 | + user: samuelcolvin | ||
| 36 | + password: | ||
| 37 | + secure: QbXFF2puEWjhFUpD0yu2R+wP4QI1IKIomBkMizsiCyMutlexERElranyYB8bsakvjPaJ+zU14ufffh2u7UA7Zhep/iE4skRHq4XWxnnRLHGu5nyGf3+zSM3F9MOzV32eZ4CDLJtFb6I0ensjTpodJH2EsIYHYxTgndIZn56Qbh6CStj7Xg1zm0Ujxdzm4ZLgcS28SOF/tpjsDW9+GXwc6L1mAZWYiS98gVgzL1vBd9tL9uFbbuFwGz9uhFMzFJko7vXSl8urWB4qeCspKXa9iKH7/AOYSwXTCwcg8U2hhC9UsOapnga2BubZKlU5HRfSs9fQcpnzcP2lwhSmkrEFa8VOw83hX6+bL564xK1Q4kanfGZ1fLU4FYge3iOnqjH7ajO7xEcUrcOEYUPfxM4EfdiDw0xnAzE1ITGH1/pZikF+wjlu+ez7RmmnejgK7quT1WU7keo7pSlRSfQtNgNl6xu818x0xZ1TScfN6e9npNy4TYyIooMOOeI4tMdfcR4JClkjGKhAtBk81DH7isZgPv3uwocGnKZ2S7La97CE3ADzU3MTA9xVIOSOjzwuvAe72uS2nwzqXkS9KATdATkC9QCvheJ9jIBB4UcqnHbD8L1gkqdmZwXZqHZldq8wcqNYZb+81lumy5EZ6xSoEzlLDpXHe80EjMUOBkb5fz3D44s= | ||
| 38 | + distributions: sdist bdist_wheel | ||
| 39 | + skip_upload_docs: true | ||
| 40 | + on: | ||
| 41 | + tags: true | ||
| 42 | + python: 3.6 | ||
| 43 | + - provider: script | ||
| 44 | + script: make publish | ||
| 45 | + on: | ||
| 46 | + tags: true | ||
| 47 | + python: 3.6 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,21 @@ | |||
| 1 | + The MIT License (MIT) | ||
| 2 | + | ||
| 3 | + Copyright (c) 2017 Samuel Colvin | ||
| 4 | + | ||
| 5 | + Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 6 | + of this software and associated documentation files (the "Software"), to deal | ||
| 7 | + in the Software without restriction, including without limitation the rights | ||
| 8 | + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 9 | + copies of the Software, and to permit persons to whom the Software is | ||
| 10 | + furnished to do so, subject to the following conditions: | ||
| 11 | + | ||
| 12 | + The above copyright notice and this permission notice shall be included in all | ||
| 13 | + copies or substantial portions of the Software. | ||
| 14 | + | ||
| 15 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 16 | + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 17 | + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 18 | + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 19 | + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 20 | + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 21 | + SOFTWARE. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,54 @@ | |||
| 1 | + .DEFAULT_GOAL := all | ||
| 2 | + | ||
| 3 | + .PHONY: install | ||
| 4 | + install: | ||
| 5 | + pip install -U setuptools pip | ||
| 6 | + pip install -r requirements.txt | ||
| 7 | + pip install -U . | ||
| 8 | + | ||
| 9 | + .PHONY: isort | ||
| 10 | + isort: | ||
| 11 | + isort -rc -w 120 devtools | ||
| 12 | + isort -rc -w 120 tests | ||
| 13 | + | ||
| 14 | + .PHONY: lint | ||
| 15 | + lint: | ||
| 16 | + python setup.py check -rms | ||
| 17 | + flake8 devtools/ tests/ | ||
| 18 | + pytest devtools -p no:sugar -q | ||
| 19 | + | ||
| 20 | + .PHONY: test | ||
| 21 | + test: | ||
| 22 | + pytest --cov=devtools | ||
| 23 | + | ||
| 24 | + .PHONY: testcov | ||
| 25 | + testcov: | ||
| 26 | + pytest --cov=devtools | ||
| 27 | + @echo "building coverage html" | ||
| 28 | + @coverage html | ||
| 29 | + | ||
| 30 | + .PHONY: all | ||
| 31 | + all: testcov lint | ||
| 32 | + | ||
| 33 | + .PHONY: clean | ||
| 34 | + clean: | ||
| 35 | + rm -rf `find . -name __pycache__` | ||
| 36 | + rm -f `find . -type f -name '*.py[co]' ` | ||
| 37 | + rm -f `find . -type f -name '*~' ` | ||
| 38 | + rm -f `find . -type f -name '.*~' ` | ||
| 39 | + rm -rf .cache | ||
| 40 | + rm -rf htmlcov | ||
| 41 | + rm -rf *.egg-info | ||
| 42 | + rm -f .coverage | ||
| 43 | + rm -f .coverage.* | ||
| 44 | + rm -rf build | ||
| 45 | + python setup.py clean | ||
| 46 | + make -C docs clean | ||
| 47 | + | ||
| 48 | + .PHONY: docs | ||
| 49 | + docs: | ||
| 50 | + make -C docs html | ||
| 51 | + | ||
| 52 | + .PHONY: docs-lint | ||
| 53 | + docs-lint: | ||
| 54 | + make -C docs lint | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,34 @@ | |||
| 1 | + python devtools | ||
| 2 | + =============== | ||
| 3 | + | ||
| 4 | + |BuildStatus| |Coverage| |pypi| | ||
| 5 | + | ||
| 6 | + Dev tools for python. **WIP** | ||
| 7 | + | ||
| 8 | + Usage | ||
| 9 | + ----- | ||
| 10 | + | ||
| 11 | + .. code:: python | ||
| 12 | + | ||
| 13 | + from devtools import debug | ||
| 14 | + | ||
| 15 | + whatever = [1, 2, 3] | ||
| 16 | + debug(whatever) | ||
| 17 | + | ||
| 18 | + Usage without Import | ||
| 19 | + -------------------- | ||
| 20 | + | ||
| 21 | + modify ``/usr/lib/python3.6/sitecustomize.py`` making ``debug`` available in any python 3.6 code | ||
| 22 | + | ||
| 23 | + .. code:: python | ||
| 24 | + | ||
| 25 | + from devtools import debug | ||
| 26 | + __builtins__['debug'] = debug | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + .. |BuildStatus| image:: https://travis-ci.org/samuelcolvin/python-devtools.svg?branch=master | ||
| 30 | + :target: https://travis-ci.org/samuelcolvin/python-devtools | ||
| 31 | + .. |Coverage| image:: https://codecov.io/gh/samuelcolvin/python-devtools/branch/master/graph/badge.svg | ||
| 32 | + :target: https://codecov.io/gh/samuelcolvin/python-devtools | ||
| 33 | + .. |pypi| image:: https://img.shields.io/pypi/v/python-devtools.svg | ||
| 34 | + :target: https://pypi.python.org/pypi/python-devtools | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,3 @@ | |||
| 1 | + # flake8: noqa | ||
| 2 | + from .debug import * | ||
| 3 | + from .version import VERSION | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,143 @@ | |||
| 1 | + import ast | ||
| 2 | + import inspect | ||
| 3 | + import re | ||
| 4 | + import warnings | ||
| 5 | + from pathlib import Path | ||
| 6 | + from textwrap import dedent | ||
| 7 | + from typing import Generator, List | ||
| 8 | + | ||
| 9 | + __all__ = ['Debug', 'debug'] | ||
| 10 | + CWD = Path('.').resolve() | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + class DebugArgument: | ||
| 14 | + __slots__ = 'value', 'name', 'extra' | ||
| 15 | + | ||
| 16 | + def __init__(self, value, *, name=None, **extra): | ||
| 17 | + self.value = value | ||
| 18 | + self.name = name | ||
| 19 | + self.extra = {k: v for k, v in extra.items() if v} | ||
| 20 | + | ||
| 21 | + def value_str(self): | ||
| 22 | + if isinstance(self.value, str): | ||
| 23 | + return '"{}"'.format(self.value) | ||
| 24 | + return str(self.value) | ||
| 25 | + | ||
| 26 | + def __str__(self) -> str: | ||
| 27 | + template = '{value} ({self.value.__class__.__name__}) {extra}' | ||
| 28 | + if self.name: | ||
| 29 | + template = '{self.name} = ' + template | ||
| 30 | + return template.format( | ||
| 31 | + self=self, | ||
| 32 | + value=self.value_str(), | ||
| 33 | + extra=' '.join('{}={}'.format(k, v) for k, v in self.extra.items()) | ||
| 34 | + ).rstrip(' ') # trailing space if extra is empty | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + class DebugOutput: | ||
| 38 | + """ | ||
| 39 | + Represents the output of a debug command. | ||
| 40 | + """ | ||
| 41 | + arg_class = DebugArgument | ||
| 42 | + __slots__ = 'filename', 'lineno', 'frame', 'arguments' | ||
| 43 | + | ||
| 44 | + def __init__(self, *, filename: str, lineno: int, frame: str, arguments: List[DebugArgument]): | ||
| 45 | + self.filename = filename | ||
| 46 | + self.lineno = lineno | ||
| 47 | + self.frame = frame | ||
| 48 | + self.arguments = arguments | ||
| 49 | + | ||
| 50 | + def __str__(self) -> str: | ||
| 51 | + template = '{s.filename}:{s.lineno} {s.frame}' | ||
| 52 | + if len(self.arguments) == 1: | ||
| 53 | + return (template + ': {a}').format(s=self, a=self.arguments[0]) | ||
| 54 | + else: | ||
| 55 | + return (template + '\n {a}').format(s=self, a='\n '.join(str(a) for a in self.arguments)) | ||
| 56 | + | ||
| 57 | + def __repr__(self) -> str: | ||
| 58 | + arguments = ' '.join(str(a) for a in self.arguments) | ||
| 59 | + return '<DebugOutput {s.filename}:{s.lineno} {s.frame} arguments: {a}>'.format(s=self, a=arguments) | ||
| 60 | + | ||
| 61 | + | ||
| 62 | + class Debug: | ||
| 63 | + output_class = DebugOutput | ||
| 64 | + | ||
| 65 | + def __call__(self, *args, **kwargs): | ||
| 66 | + print(self._process(args, kwargs), flush=True) | ||
| 67 | + | ||
| 68 | + def format(self, *args, **kwargs): | ||
| 69 | + return self._process(args, kwargs) | ||
| 70 | + | ||
| 71 | + def _process(self, args, kwargs): | ||
| 72 | + curframe = inspect.currentframe() | ||
| 73 | + frames = inspect.getouterframes(curframe, context=20) | ||
| 74 | + # BEWARE: this must be call by a method which in turn is called "directly" for the frame to be correct | ||
| 75 | + call_frame = frames[2] | ||
| 76 | + | ||
| 77 | + filename = call_frame.filename | ||
| 78 | + if filename.startswith('/'): | ||
| 79 | + # make the path relative | ||
| 80 | + try: | ||
| 81 | + filename = str(Path(filename).relative_to(CWD)) | ||
| 82 | + except ValueError: | ||
| 83 | + # happens if filename path is not within CWD | ||
| 84 | + pass | ||
| 85 | + | ||
| 86 | + call_lines = [] | ||
| 87 | + for line in range(call_frame.index, 0, -1): | ||
| 88 | + new_line = call_frame.code_context[line] | ||
| 89 | + call_lines.append(new_line) | ||
| 90 | + if re.search('debug *\(', new_line): | ||
| 91 | + break | ||
| 92 | + call_lines.reverse() | ||
| 93 | + | ||
| 94 | + return self.output_class( | ||
| 95 | + filename=filename, | ||
| 96 | + lineno=call_frame.lineno - len(call_lines), | ||
| 97 | + frame=call_frame.function, | ||
| 98 | + arguments=list(self._process_args(call_lines, args, kwargs)) | ||
| 99 | + ) | ||
| 100 | + | ||
| 101 | + def _process_args(self, call_lines, args, kwargs) -> Generator[DebugArgument, None, None]: # noqa: C901 | ||
| 102 | + code = dedent(''.join(call_lines)) | ||
| 103 | + func_ast = ast.parse(code).body[0].value | ||
| 104 | + | ||
| 105 | + arg_offsets = list(self._get_offsets(func_ast)) | ||
| 106 | + for arg, ast_node, i in zip(args, func_ast.args, range(1000)): | ||
| 107 | + if isinstance(ast_node, ast.Name): | ||
| 108 | + yield self.output_class.arg_class(arg, name=ast_node.id) | ||
| 109 | + elif isinstance(ast_node, ast.Str): | ||
| 110 | + yield self.output_class.arg_class(arg, len=len(arg)) | ||
| 111 | + elif isinstance(ast_node, (ast.Num, ast.List, ast.Dict, ast.Set)): | ||
| 112 | + yield self.output_class.arg_class(arg) | ||
| 113 | + elif isinstance(ast_node, (ast.Call, ast.Compare)): | ||
| 114 | + # TODO replace this hack with astor when it get's a new release | ||
| 115 | + end = -2 | ||
| 116 | + try: | ||
| 117 | + next_line, next_offset = arg_offsets[i + 1] | ||
| 118 | + if next_line == ast_node.lineno: | ||
| 119 | + end = next_offset | ||
| 120 | + except IndexError: | ||
| 121 | + pass | ||
| 122 | + name = call_lines[ast_node.lineno - 1][ast_node.col_offset:end] | ||
| 123 | + yield self.output_class.arg_class(arg, name=name.strip(' ,')) | ||
| 124 | + else: | ||
| 125 | + warnings.warn('Unknown type: {}'.format(ast.dump(ast_node)), category=RuntimeError) | ||
| 126 | + yield self.output_class.arg_class(arg) | ||
| 127 | + | ||
| 128 | + kw_arg_names = {} | ||
| 129 | + for kw in func_ast.keywords: | ||
| 130 | + if isinstance(kw.value, ast.Name): | ||
| 131 | + kw_arg_names[kw.arg] = kw.value.id | ||
| 132 | + for name, value in kwargs.items(): | ||
| 133 | + yield self.output_class.arg_class(value, name=name, variable=kw_arg_names.get(name)) | ||
| 134 | + | ||
| 135 | + @classmethod | ||
| 136 | + def _get_offsets(cls, func_ast): | ||
| 137 | + for arg in func_ast.args: | ||
| 138 | + yield arg.lineno, arg.col_offset | ||
| 139 | + for kw in func_ast.keywords: | ||
| 140 | + yield kw.value.lineno, kw.value.col_offset - len(kw.arg) - 1 | ||
| 141 | + | ||
| 142 | + | ||
| 143 | + debug = Debug() | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,5 @@ | |||
| 1 | + from distutils.version import StrictVersion | ||
| 2 | + | ||
| 3 | + __all__ = ['VERSION'] | ||
| 4 | + | ||
| 5 | + VERSION = StrictVersion('0.0.1') | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,4 @@ | |||
| 1 | + #-r docs/requirements.txt | ||
| 2 | + -r tests/requirements.txt | ||
| 3 | + | ||
| 4 | + ipython # pyup: ignore | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments