pax_global_header00006660000000000000000000000064127765375540014537gustar00rootroot0000000000000052 comment=b084e6297eed8e3f5158ef458adda05752bad109 logfury-0.1.2/000077500000000000000000000000001277653755400132265ustar00rootroot00000000000000logfury-0.1.2/.gitignore000066400000000000000000000000511277653755400152120ustar00rootroot00000000000000*.pyc .idea *.egg-info build dist .eggs/ logfury-0.1.2/.travis.yml000066400000000000000000000246121277653755400153440ustar00rootroot00000000000000notifications: slack: secure: naix0bLa/50X1HIXwLtkUC/YH9Iurs18eVy/mhfrfD2YFd7QjUGRz24SQU2r0ev0Qh3CUaH9MJoAUbu4oaXGB1VXzZkV7OIGimZe4gcWJuzItnVhyrQJKpcpC7yafdl7uDA0wjWs2BYRHCJ3c35Gv4SmihuGVXUS3WFsL0i3VyXno8xPOfmyuUT7GZWW80uMnV6eOyhbPCBb17CchI8x7owdXVf3drxCrrjoP3oNbbpByTsDC+hnCmVQbhkZAqS63gZRj5LT6gNh4ZA/qenaGkhzfHFRSUW1+H16ZzYcSSl0PfAQBQYrc7agUjI987PBHdOv6BJS+pKmZ0MvaDwbnzhHZZdb8LXqM5K0zAhPiZMPL2MTq7+eNOBdRuTlbWs3OZfbY1cnewSpMmUGfwrIjVnLtvfhfJXVaFbpIfvNc6eafgmHas23A05f4Gk/IcRLvqICw+GQg76ADUpWubQal7gECTGbeivOdRLnQcQYWCg/a5VR5B+4ELENmWLRD7IQgo6rJKpPiRdGRc0C0E9Mqt3w7w+9NiDnjTbSu8Q6ARBLXjAu+jSbY1VK0iPq6k1Wj8U5EgQF/xVLCEtKDRW1bdllwWNgi2yPo1CrkgKmJj6XPU3Zc744NR85uC+PxAjkIIbm90joevPmUQGwkpAgVX2pxCjVAX6ynQ79TFfdd/Y= language: python python: - 2.6 - 2.7 - 3.3 - 3.4 - 3.5 - nightly - pypy - pypy3 branches: # https://github.com/travis-ci/travis-ci/issues/1147#issuecomment-160820262 # this reduces workload on Travis if there are many pushes in a short time. # It does not have a drawback if: # - pull requests are used for review # - branches that we care about (not work-in-progress) are listed below only: - master matrix: include: # Source Code Analysis is slow. We just run it once on # Python 2 and once on Python 3. - python: 2.7 env: MODE=SCA - python: 3.5 env: MODE=SCA # Travis CI doesn't support OSX python builds officially, # but that won't stop us - python: 2.7 os: osx language: generic env: OS_X_PYTHON_VERSION=python - python: 3.5 os: osx language: generic env: OS_X_PYTHON_VERSION=python3 # jython - python: 2.7 env: JYTHON=true # pypy - python: pypy env: PYPY_VERSION=pypy-4.0.1 - python: pypy env: PYPY_VERSION=pypy-5.1 - python: pypy3 env: PYPY_VERSION=pypy3-2.4.0 # coverage - python: 3.5 env: MODE=coverage cache: - pip - directories: - $HOME/.eggs # custom pypy versions - $HOME/.pyenv # jython - $HOME/.virtualenv/jython - $HOME/jython - $HOME/jython-pip # OS X - $HOME/.virtualenv/osx-python - $HOME/.virtualenv/osx-python3 #- /usr/local/Cellar/python # see comment in the install script #- /usr/local/Cellar/python3 before_install: - uname -a - if [[ "$TRAVIS_OS_NAME" != "osx" ]]; then lsb_release -a; fi - | # install python on OS X if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then if [ -n 'ALWAYS TRUE!' -o ! -d "/usr/local/Cellar/$OS_X_PYTHON_VERSION" ]; then # unfortunately caching /usr/local/Cellar/$OS_X_PYTHON_VERSION is not enough. # brew does some kind of side effect which registers the binary in the system # or something. The resulting error error is: # # dyld: Library not loaded: @executable_path/../.Python # # Due to lack of information on the web as well as the lack of appropriate # environment it is very hard to debug out how to properly cache the python # installation on OS X. # # Therefore caching is temporarily disabled until someone figures it out. # Typically brew update and installation of python takes 3 to 4 minutes. brew update brew install "$OS_X_PYTHON_VERSION" else echo 'skip install' fi if [ ! -e "$HOME/.virtualenv/osx-$OS_X_PYTHON_VERSION/bin/activate" ]; then virtualenv -p "$OS_X_PYTHON_VERSION" "$HOME/.virtualenv/osx-$OS_X_PYTHON_VERSION" else echo 'skip virtualenv' fi source "$HOME/.virtualenv/osx-$OS_X_PYTHON_VERSION/bin/activate" else echo skip fi - | # get jython if [ -n "$JYTHON" ]; then if [ ! -f "$HOME/.virtualenv/jython/bin/activate" ]; then wget http://search.maven.org/remotecontent?filepath=org/python/jython-installer/2.7.1b3/jython-installer-2.7.1b3.jar -O jython-installer-2.7.1b3.jar java -jar jython-installer-2.7.1b3.jar --silent --directory "$HOME/jython" else echo skip fi # install a custom version of pip, as standard pip doesn't work on jython (https://github.com/jythontools/pip/commits/develop) if [ ! -d "$HOME/jython-pip" ]; then mkdir ~/jython-pip else echo skip fi if [ ! -f ~/jython-pip/pip-7.1.2-py2.py3-none-any.whl ]; then wget https://pypi.python.org/packages/py2.py3/p/pip/pip-7.1.2-py2.py3-none-any.whl -O ~/jython-pip/pip-7.1.2-py2.py3-none-any.whl else echo skip fi # create jython virtualenv if [ ! -f "$HOME/.virtualenv/jython/bin/activate" ]; then virtualenv --system-site-packages --extra-search-dir="$HOME/jython-pip" -p "$HOME/jython/bin/jython" "$HOME/.virtualenv/jython" else echo skip fi source "$HOME/.virtualenv/jython/bin/activate" else echo skip fi - | # upgrade pypy (to a version that works with Cryptography 1.0) if [ -z "$PYPY_VERSION" ]; then echo 'this is a build of native Travis pypy/pypy3, not the upgraded one' elif [[ "$TRAVIS_PYTHON_VERSION" == "pypy" ]] || [[ "$TRAVIS_PYTHON_VERSION" == "pypy3" ]]; then export PYENV_ROOT="$HOME/.pyenv" if [ -f "$PYENV_ROOT/bin/pyenv" ]; then pushd "$PYENV_ROOT" && git pull && popd else rm -rf "$PYENV_ROOT" && git clone --depth 1 https://github.com/yyuu/pyenv.git "$PYENV_ROOT" fi echo "PYPY_VERSION=$PYPY_VERSION" "$PYENV_ROOT/bin/pyenv" install --skip-existing "$PYPY_VERSION" if [ ! -e "$HOME/.virtualenv/$PYPY_VERSION/bin/activate" ]; then virtualenv -p "$PYENV_ROOT/versions/$PYPY_VERSION/bin/python" "$HOME/.virtualenv/$PYPY_VERSION" fi source "$HOME/.virtualenv/$PYPY_VERSION/bin/activate" else echo skip fi - virtualenv --version - python --version install: - | # install PyYAML on Jython manually before pip tries to do it and fails if [ -n "$JYTHON" -a ! -f "$HOME/.virtualenv/jython/Lib/site-packages/PyYAML-3.11-py2.7.egg-info" ] && egrep '^pyyaml((>|<|=).+)?$' requirements*.txt -q; then wget https://pypi.python.org/packages/source/P/PyYAML/PyYAML-3.11.tar.gz tar -zxf PyYAML-3.11.tar.gz cd PyYAML-3.11/ wget http://pyyaml.org/raw-attachment/ticket/163/jython-setup.patch patch < jython-setup.patch python setup.py install cd - fi # installing regex on jython fails (jython can't compile the native extension), this awk filter prevents it - cat requirements*.txt | awk \"${JYTHON:-False}'" == "True" && /^regex/ || ! /^regex/' | xargs pip install - | # coverage has additional requirements if [ "$MODE" == 'coverage' ]; then pip install coverage if [ -n "$CODACY_PROJECT_TOKEN" ]; then pip install codacy-coverage; fi if [ -n "$COVERALLS_REPO_TOKEN" ]; then pip install coveralls; fi if [ -n "$CODECOV_TOKEN" ]; then wget https://codecov.io/bash -O codecov.sh && chmod +x codecov.sh; fi fi before_script: - pip freeze # Before runing the test case, we need to make jython run some code as in first run it can put something on stdout - if [ -n "$JYTHON" ]; then python -c "print ''"; else echo skip; fi script: - | # nosetests if [ -z "$MODE" ]; then python setup.py nosetests elif [ "$MODE" == 'coverage' ]; then python setup.py nosetests --with-coverage --cover-xml --cover-package "$(python setup.py --name)" --cover-branches else echo skip fi - | # yapf if [ "$MODE" == 'SCA' ]; then if [ -n "$TRAVIS_COMMIT_RANGE" ]; then echo "using commit range: $TRAVIS_COMMIT_RANGE" git diff --name-only "$TRAVIS_COMMIT_RANGE" | grep '\.py$' | xargs yapf --diff setup.py || exit else yapf --diff --recursive . || exit fi else echo skip fi - if [ "$MODE" == 'SCA' ]; then pyflakes . || exit; else echo skip; fi # isort doesn't get along with yapf. Hopefully we'll figure it out and re-enable in the future. # - if [ "$MODE" == 'SCA' ]; then isort --order-by-type --recursive --line-width 100 --diff --verbose --check-only || exit; else echo skip; fi # home-made bad code detectors - if [ "$MODE" == 'SCA' ]; then egrep -r '^ *class [^\(]+(\(\))?:' . && exit 1 || true; else echo skip; fi # old-style class detector - if [ "$MODE" == 'SCA' ]; then grep -r 'isinstance\(' . && exit 1 || true; else echo skip; fi # very non-pythonic - if [ "$MODE" == 'SCA' ]; then egrep -r '\b(all|any)\(\[' . && exit 1 || true; else echo skip; fi # a list is unnecesarily allocated where a generator expression could have been used - if [ "$MODE" == 'SCA' ]; then grep -r '\_\_metaclass\_\_' . && exit 1 || true; else echo skip; fi # bad metaclass declaration: use six - if [ "$MODE" == 'SCA' ]; then grep -P -r '^ *logger = (?!logging\.getLogger\(__name__\)$)' . | grep -v '.eggs/nose-' && exit 1 || true; else echo skip; fi # bad logger definition: use `logger = logging.getLogger(__name__)`. See the magic logging metaclass. - if [ "$MODE" == 'SCA' ]; then python setup.py check -r -s; else echo skip; fi - if [ "$MODE" == 'SCA' ]; then check-manifest -v; else echo skip; fi # package installation - | if [ "$MODE" == 'SCA' ]; then echo skip else if [ -n "$OS_X_PYTHON_VERSION" ]; then # install action fails on osx for unknown reason python setup.py develop else python setup.py install fi fi after_success: - if [ "$MODE" == 'coverage' -a -n "$CODACY_PROJECT_TOKEN" ]; then python-codacy-coverage -r coverage.xml; else echo skip; fi - if [ "$MODE" == 'coverage' -a -n "$COVERALLS_REPO_TOKEN" ]; then coveralls; else echo skip; fi - if [ "$MODE" == 'coverage' -a -n "$CODECOV_TOKEN" ]; then ./codecov.sh -X gcov -X coveragepy; else echo skip; fi logfury-0.1.2/LICENSE.txt000066400000000000000000000027361277653755400150610ustar00rootroot00000000000000BSD 3-clause license Copyright (c) 2016, Pawel Polewicz. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of logfury nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. logfury-0.1.2/MANIFEST.in000066400000000000000000000002071277653755400147630ustar00rootroot00000000000000include requirements.txt include requirements-test.txt include requirements-setup.txt include LICENSE.txt include README.rst graft src logfury-0.1.2/README.rst000066400000000000000000000077141277653755400147260ustar00rootroot00000000000000======== Logfury ======== Logfury is for python library maintainers. It allows for responsible, low-boilerplate logging of method calls. ***************** License ***************** BSD 3-clause ***************************** whats with the weird import ***************************** .. sourcecode:: python from logfury.v0_1 import DefaultTraceMeta If you were to use logfury in your library, any change to the API could potentially break your program. Nobody wants that. Thanks to this import trick I can keep the 0.1.x API very stable. At the same time I can change the functionality of the library and change default behavior of version 0.2.x etc, without changing the name of the package. This way YOU decide when to adopt potentially incompatible API changes, by incrementing the API version on import. ***************** Installation ***************** ^^^^^^^^^^^^^^^^^^^^ Current stable ^^^^^^^^^^^^^^^^^^^^ :: pip install logfury ^^^^^^^^^^^^^^^^^^^^ Development version ^^^^^^^^^^^^^^^^^^^^ :: git clone git@github.com:ppolewicz/logfury.git python setup.py install ***************** Basic usage ***************** ^^^^^^^^^^^^^^^^^^^^^^^^^^^ DefaultTraceMeta metaclass ^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. sourcecode:: pycon >>> import logging >>> import six >>> >>> from logfury.v0_1 import DefaultTraceMeta, limit_trace_arguments, disable_trace >>> >>> >>> logging.basicConfig() >>> logger = logging.getLogger(__name__) >>> logger.setLevel(logging.DEBUG) >>> >>> >>> @six.add_metaclass(DefaultTraceMeta) >>> class Foo(object): ... def baz(self, a, b, c=None): ... return True ... def get_blah(self): ... return 5 ... def _hello(self): ... pass ... @disable_trace ... def world(self): ... pass ... def __repr__(self): ... return '<%s object>' % (self.__class__.__name__,) ... >>> class Bar(Foo): ... def baz(self, a, b, c=None): ... b += 1 ... return super(Bar, self).baz(a, b, c) ... def world(self): ... pass ... @limit_trace_arguments(skip=['password']) ... def secret(self, password, other): ... pass ... @limit_trace_arguments(only=['other']) ... def secret2(self, password, other): ... pass ... >>> a = Foo() >>> a.baz(1, 2, 3) DEBUG:__main__:calling Foo.baz(self=, a=1, b=2, c=3) >>> a.baz(4, b=8) DEBUG:__main__:calling Foo.baz(self=, a=4, b=8) >>> a.get_blah() # nothing happens, since v0_1.DefaultTraceMeta does not trace "get_.*" >>> a._hello() # nothing happens, since v0_1.DefaultTraceMeta does not trace "_.*" >>> a.world() # nothing happens, since v0_1.DefaultTraceMeta does not trace "_.*" >>> b = Bar() >>> b.baz(4, b=8) # tracing is inherited DEBUG:__main__:calling Bar.baz(self=, a=4, b=8) DEBUG:__main__:calling Foo.baz(self=, a=4, b=9, c=None) >>> b.world() # nothing happens, since Foo.world() tracing was disabled and Bar inherited it >>> b.secret('correct horse battery staple', 'Hello world!') DEBUG:__main__:calling Bar.secret(self=, other='Hello world!') (hidden args: password) >>> b.secret2('correct horse battery staple', 'Hello world!') DEBUG:__main__:calling Bar.secret2(other='Hello world!') (hidden args: self, password) ^^^^^^^^^^^^^^^^^^^^ trace_call decorator ^^^^^^^^^^^^^^^^^^^^ .. sourcecode:: pycon >>> import logging >>> from logfury import * >>> logging.basicConfig() >>> logger = logging.getLogger(__name__) >>> >>> @trace_call(logger) ... def foo(a, b, c=None): ... return True ... >>> foo(1, 2, 3) True >>> logger.setLevel(logging.DEBUG) >>> foo(1, 2, 3) DEBUG:__main__:calling foo(a=1, b=2, c=3) True >>> foo(1, b=2) DEBUG:__main__:calling foo(a=1, b=2) True >>> logfury-0.1.2/appveyor.yml000066400000000000000000000073761277653755400156330ustar00rootroot00000000000000# taken from https://github.com/ogrisel/python-appveyor-demo/blob/master/appveyor.yml under http://creativecommons.org/publicdomain/zero/1.0/ environment: global: # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the # /E:ON and /V:ON options are not enabled in the batch script intepreter # See: http://stackoverflow.com/a/13751649/163740 CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\ci\\appveyor\\run_with_env.cmd" matrix: # Python 2.7.10 is the latest version and is not pre-installed. #- PYTHON: "C:\\Python27.10" # PYTHON_VERSION: "2.7.10" # PYTHON_ARCH: "32" #- PYTHON: "C:\\Python27.10-x64" # PYTHON_VERSION: "2.7.10" # PYTHON_ARCH: "64" # Pre-installed Python versions, which Appveyor may upgrade to # a later point release. # See: http://www.appveyor.com/docs/installed-software#python #- PYTHON: "C:\\Python27" # PYTHON_VERSION: "2.7.x" # currently 2.7.9 # PYTHON_ARCH: "32" #- PYTHON: "C:\\Python27-x64" # PYTHON_VERSION: "2.7.x" # currently 2.7.9 # PYTHON_ARCH: "64" #- PYTHON: "C:\\Python33" # PYTHON_VERSION: "3.3.x" # currently 3.3.5 # PYTHON_ARCH: "32" #- PYTHON: "C:\\Python33-x64" # PYTHON_VERSION: "3.3.x" # currently 3.3.5 # PYTHON_ARCH: "64" #- PYTHON: "C:\\Python34" # PYTHON_VERSION: "3.4.x" # currently 3.4.3 # PYTHON_ARCH: "32" #- PYTHON: "C:\\Python34-x64" # PYTHON_VERSION: "3.4.x" # currently 3.4.3 # PYTHON_ARCH: "64" # Python versions not pre-installed # Python 2.6.6 is the latest Python 2.6 with a Windows installer # See: https://github.com/ogrisel/python-appveyor-demo/issues/10 - PYTHON: "C:\\Python266" PYTHON_VERSION: "2.6.6" PYTHON_ARCH: "32" #- PYTHON: "C:\\Python266-x64" # PYTHON_VERSION: "2.6.6" # PYTHON_ARCH: "64" #- PYTHON: "C:\\Python35" # PYTHON_VERSION: "3.5.0" # PYTHON_ARCH: "32" - PYTHON: "C:\\Python35-x64" PYTHON_VERSION: "3.5.0" PYTHON_ARCH: "64" # Major and minor releases (i.e x.0.0 and x.y.0) prior to 3.3.0 use # a different naming scheme. #- PYTHON: "C:\\Python270" # PYTHON_VERSION: "2.7.0" # PYTHON_ARCH: "32" #- PYTHON: "C:\\Python270-x64" # PYTHON_VERSION: "2.7.0" # PYTHON_ARCH: "64" install: - ECHO "Filesystem root:" - ps: "ls \"C:/\"" - ECHO "Installed SDKs:" - ps: "ls \"C:/Program Files/Microsoft SDKs/Windows\"" # Install Python (from the official .msi of http://python.org) and pip when # not already installed. - ps: if (-not(Test-Path($env:PYTHON))) { & ci\appveyor\install.ps1 } # Prepend newly installed Python to the PATH of this build (this cannot be # done from inside the powershell script as it would require to restart # the parent CMD process). - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" # Check that we have the expected version and architecture for Python - "python --version" - "python -c \"import struct; print(struct.calcsize('P') * 8)\"" # Upgrade to the latest version of pip to avoid it displaying warnings # about it being out of date. - "pip install --disable-pip-version-check --user --upgrade pip" # Install the build dependencies of the project. If some dependencies contain # compiled extensions and are not provided as pre-built wheel packages, # pip will build them from source using the MSVC compiler matching the # target Python version and architecture - "%CMD_IN_ENV% pip install -r requirements.txt" - "%CMD_IN_ENV% pip install -r requirements-test.txt" - "%CMD_IN_ENV% pip install -r requirements-setup.txt" build_script: # Build the compiled extension - "%CMD_IN_ENV% python setup.py build" test_script: # Run the project tests - "%CMD_IN_ENV% python setup.py nosetests --processes=0" logfury-0.1.2/ci/000077500000000000000000000000001277653755400136215ustar00rootroot00000000000000logfury-0.1.2/ci/appveyor/000077500000000000000000000000001277653755400154665ustar00rootroot00000000000000logfury-0.1.2/ci/appveyor/install.ps1000066400000000000000000000160331277653755400175640ustar00rootroot00000000000000# Sample script to install Python and pip under Windows # Authors: Olivier Grisel, Jonathan Helmus, Kyle Kastner, and Alex Willmer # License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ $MINICONDA_URL = "http://repo.continuum.io/miniconda/" $BASE_URL = "https://www.python.org/ftp/python/" $GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" $GET_PIP_PATH = "C:\get-pip.py" $PYTHON_PRERELEASE_REGEX = @" (?x) (?\d+) \. (?\d+) \. (?\d+) (?[a-z]{1,2}\d+) "@ function Download ($filename, $url) { $webclient = New-Object System.Net.WebClient $basedir = $pwd.Path + "\" $filepath = $basedir + $filename if (Test-Path $filename) { Write-Host "Reusing" $filepath return $filepath } # Download and retry up to 3 times in case of network transient errors. Write-Host "Downloading" $filename "from" $url $retry_attempts = 2 for ($i = 0; $i -lt $retry_attempts; $i++) { try { $webclient.DownloadFile($url, $filepath) break } Catch [Exception]{ Start-Sleep 1 } } if (Test-Path $filepath) { Write-Host "File saved at" $filepath } else { # Retry once to get the error message if any at the last try $webclient.DownloadFile($url, $filepath) } return $filepath } function ParsePythonVersion ($python_version) { if ($python_version -match $PYTHON_PRERELEASE_REGEX) { return ([int]$matches.major, [int]$matches.minor, [int]$matches.micro, $matches.prerelease) } $version_obj = [version]$python_version return ($version_obj.major, $version_obj.minor, $version_obj.build, "") } function DownloadPython ($python_version, $platform_suffix) { $major, $minor, $micro, $prerelease = ParsePythonVersion $python_version if (($major -le 2 -and $micro -eq 0) ` -or ($major -eq 3 -and $minor -le 2 -and $micro -eq 0) ` ) { $dir = "$major.$minor" $python_version = "$major.$minor$prerelease" } else { $dir = "$major.$minor.$micro" } if ($prerelease) { if (($major -le 2) ` -or ($major -eq 3 -and $minor -eq 1) ` -or ($major -eq 3 -and $minor -eq 2) ` -or ($major -eq 3 -and $minor -eq 3) ` ) { $dir = "$dir/prev" } } if (($major -le 2) -or ($major -le 3 -and $minor -le 4)) { $ext = "msi" if ($platform_suffix) { $platform_suffix = ".$platform_suffix" } } else { $ext = "exe" if ($platform_suffix) { $platform_suffix = "-$platform_suffix" } } $filename = "python-$python_version$platform_suffix.$ext" $url = "$BASE_URL$dir/$filename" $filepath = Download $filename $url return $filepath } function InstallPython ($python_version, $architecture, $python_home) { Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home if (Test-Path $python_home) { Write-Host $python_home "already exists, skipping." return $false } if ($architecture -eq "32") { $platform_suffix = "" } else { $platform_suffix = "amd64" } $installer_path = DownloadPython $python_version $platform_suffix $installer_ext = [System.IO.Path]::GetExtension($installer_path) Write-Host "Installing $installer_path to $python_home" $install_log = $python_home + ".log" if ($installer_ext -eq '.msi') { InstallPythonMSI $installer_path $python_home $install_log } else { InstallPythonEXE $installer_path $python_home $install_log } if (Test-Path $python_home) { Write-Host "Python $python_version ($architecture) installation complete" } else { Write-Host "Failed to install Python in $python_home" Get-Content -Path $install_log Exit 1 } } function InstallPythonEXE ($exepath, $python_home, $install_log) { $install_args = "/quiet InstallAllUsers=1 TargetDir=$python_home" RunCommand $exepath $install_args } function InstallPythonMSI ($msipath, $python_home, $install_log) { $install_args = "/qn /log $install_log /i $msipath TARGETDIR=$python_home" $uninstall_args = "/qn /x $msipath" RunCommand "msiexec.exe" $install_args if (-not(Test-Path $python_home)) { Write-Host "Python seems to be installed else-where, reinstalling." RunCommand "msiexec.exe" $uninstall_args RunCommand "msiexec.exe" $install_args } } function RunCommand ($command, $command_args) { Write-Host $command $command_args Start-Process -FilePath $command -ArgumentList $command_args -Wait -Passthru } function InstallPip ($python_home) { $pip_path = $python_home + "\Scripts\pip.exe" $python_path = $python_home + "\python.exe" if (-not(Test-Path $pip_path)) { Write-Host "Installing pip..." $webclient = New-Object System.Net.WebClient $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) Write-Host "Executing:" $python_path $GET_PIP_PATH & $python_path $GET_PIP_PATH } else { Write-Host "pip already installed." } } function DownloadMiniconda ($python_version, $platform_suffix) { if ($python_version -eq "3.4") { $filename = "Miniconda3-3.5.5-Windows-" + $platform_suffix + ".exe" } else { $filename = "Miniconda-3.5.5-Windows-" + $platform_suffix + ".exe" } $url = $MINICONDA_URL + $filename $filepath = Download $filename $url return $filepath } function InstallMiniconda ($python_version, $architecture, $python_home) { Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home if (Test-Path $python_home) { Write-Host $python_home "already exists, skipping." return $false } if ($architecture -eq "32") { $platform_suffix = "x86" } else { $platform_suffix = "x86_64" } $filepath = DownloadMiniconda $python_version $platform_suffix Write-Host "Installing" $filepath "to" $python_home $install_log = $python_home + ".log" $args = "/S /D=$python_home" Write-Host $filepath $args Start-Process -FilePath $filepath -ArgumentList $args -Wait -Passthru if (Test-Path $python_home) { Write-Host "Python $python_version ($architecture) installation complete" } else { Write-Host "Failed to install Python in $python_home" Get-Content -Path $install_log Exit 1 } } function InstallMinicondaPip ($python_home) { $pip_path = $python_home + "\Scripts\pip.exe" $conda_path = $python_home + "\Scripts\conda.exe" if (-not(Test-Path $pip_path)) { Write-Host "Installing pip..." $args = "install --yes pip" Write-Host $conda_path $args Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru } else { Write-Host "pip already installed." } } function main () { InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON InstallPip $env:PYTHON } main logfury-0.1.2/ci/appveyor/run_with_env.cmd000066400000000000000000000064461277653755400206740ustar00rootroot00000000000000:: To build extensions for 64 bit Python 3, we need to configure environment :: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) :: :: To build extensions for 64 bit Python 2, we need to configure environment :: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: :: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) :: :: 32 bit builds, and 64-bit builds for 3.5 and beyond, do not require specific :: environment configurations. :: :: Note: this script needs to be run with the /E:ON and /V:ON flags for the :: cmd interpreter, at least for (SDK v7.0) :: :: More details at: :: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows :: http://stackoverflow.com/a/13751649/163740 :: :: Author: Olivier Grisel :: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ :: :: Notes about batch files for Python people: :: :: Quotes in values are literally part of the values: :: SET FOO="bar" :: FOO is now five characters long: " b a r " :: If you don't want quotes, don't include them on the right-hand side. :: :: The CALL lines at the end of this file look redundant, but if you move them :: outside of the IF clauses, they do not run properly in the SET_SDK_64==Y :: case, I don't know why. @ECHO OFF SET COMMAND_TO_RUN=%* SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows SET WIN_WDK=c:\Program Files (x86)\Windows Kits\10\Include\wdf :: Extract the major and minor versions, and allow for the minor version to be :: more than 9. This requires the version number to have two dots in it. SET MAJOR_PYTHON_VERSION=%PYTHON_VERSION:~0,1% IF "%PYTHON_VERSION:~3,1%" == "." ( SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,1% ) ELSE ( SET MINOR_PYTHON_VERSION=%PYTHON_VERSION:~2,2% ) :: Based on the Python version, determine what SDK version to use, and whether :: to set the SDK for 64-bit. IF %MAJOR_PYTHON_VERSION% == 2 ( SET WINDOWS_SDK_VERSION="v7.0" SET SET_SDK_64=Y ) ELSE ( IF %MAJOR_PYTHON_VERSION% == 3 ( SET WINDOWS_SDK_VERSION="v7.1" IF %MINOR_PYTHON_VERSION% LEQ 4 ( SET SET_SDK_64=Y ) ELSE ( SET SET_SDK_64=N IF EXIST "%WIN_WDK%" ( :: See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ REN "%WIN_WDK%" 0wdf ) ) ) ELSE ( ECHO Unsupported Python version: "%MAJOR_PYTHON_VERSION%" EXIT 1 ) ) IF %PYTHON_ARCH% == 64 ( IF %SET_SDK_64% == Y ( ECHO Configuring Windows SDK %WINDOWS_SDK_VERSION% for Python %MAJOR_PYTHON_VERSION% on a 64 bit architecture SET DISTUTILS_USE_SDK=1 SET MSSdk=1 "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) ELSE ( ECHO Using default MSVC build environment for 64 bit architecture ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) ) ELSE ( ECHO Using default MSVC build environment for 32 bit architecture ECHO Executing: %COMMAND_TO_RUN% call %COMMAND_TO_RUN% || EXIT 1 ) logfury-0.1.2/requirements-setup.txt000066400000000000000000000000051277653755400176430ustar00rootroot00000000000000nose logfury-0.1.2/requirements-test.txt000066400000000000000000000000721277653755400174660ustar00rootroot00000000000000check-manifest pyflakes readme_renderer testfixtures yapf logfury-0.1.2/requirements.txt000066400000000000000000000000231277653755400165050ustar00rootroot00000000000000six>=1.10 funcsigs logfury-0.1.2/run-unit-tests.sh000077500000000000000000000005411277653755400165060ustar00rootroot00000000000000#!/bin/bash -e if [ -z "${PYTHON_VIRTUAL_ENVS}" ]; then python setup.py nosetests else for virtual_env in ${PYTHON_VIRTUAL_ENVS} do echo Activating ${virtual_env} source ${virtual_env}/bin/activate python setup.py nosetests done fi echo echo '#################' echo '# unit tests OK #' echo '#################' logfury-0.1.2/setup.cfg000066400000000000000000000006061277653755400150510ustar00rootroot00000000000000[metadata] description-file=README.rst [yapf] based_on_style=facebook COLUMN_LIMIT=140 SPACE_BETWEEN_ENDING_COMMA_AND_CLOSING_BRACKET=False SPLIT_PENALTY_AFTER_OPENING_BRACKET=0 [nosetests] verbosity=2 where=test processes=-1 [check-manifest] ignore = run-unit-tests.sh pre-commit.sh test test/* ci ci/* .travis.yml appveyor.yml *.pyc __pycache__ [bdist_wheel] universal=1 logfury-0.1.2/setup.py000066400000000000000000000060741277653755400147470ustar00rootroot00000000000000from codecs import open import os.path from setuptools import setup, find_packages import sys ################################################################### yapf: disable NAME = 'logfury' VERSION = '0.1.2' AUTHOR = 'Pawel Polewicz' AUTHOR_EMAIL = 'p.polewicz@gmail.com' DESCRIPTION = 'Toolkit for responsible, low-boilerplate logging of library method calls', LICENSE = 'BSD' KEYWORDS = ['logging', 'tracing'] URL = 'https://github.com/ppolewicz/logfury' DOWNLOAD_URL_TEMPLATE = URL + '/tarball/%s' CLASSIFIERS = [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Logging', 'Natural Language :: English', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: Jython', 'Programming Language :: Python :: Implementation :: PyPy', ] ################################################################### yapf: enable if __name__ == '__main__': here = os.path.abspath(os.path.dirname(__file__)) def read_file_contents(filename): with open(os.path.join(here, filename), 'rb', encoding='utf-8') as f: return f.read() long_description = read_file_contents('README.rst') requirements_install = read_file_contents('requirements.txt').splitlines() if sys.version_info == (2, 6): requirements_install.append('ordereddict') requirements_test = read_file_contents('requirements-test.txt').splitlines() setup( name = NAME, version = VERSION, url = URL, download_url = DOWNLOAD_URL_TEMPLATE % (VERSION,), author = AUTHOR, author_email = AUTHOR_EMAIL, maintainer = AUTHOR, maintainer_email = AUTHOR_EMAIL, packages = find_packages(where='src'), license = LICENSE, description = DESCRIPTION, long_description = long_description, keywords = KEYWORDS, package_dir = {'': 'src'}, zip_safe = False, classifiers = CLASSIFIERS, install_requires = requirements_install, tests_require = requirements_test, package_data = { NAME: [ 'requirements.txt', 'requirements-test.txt', ] }, # to install: pip install -e .[dev,test] extras_require = { 'test': requirements_test, }, ) # yapf: disable logfury-0.1.2/src/000077500000000000000000000000001277653755400140155ustar00rootroot00000000000000logfury-0.1.2/src/logfury/000077500000000000000000000000001277653755400155045ustar00rootroot00000000000000logfury-0.1.2/src/logfury/__init__.py000066400000000000000000000000001277653755400176030ustar00rootroot00000000000000logfury-0.1.2/src/logfury/v0_1/000077500000000000000000000000001277653755400162515ustar00rootroot00000000000000logfury-0.1.2/src/logfury/v0_1/__init__.py000066400000000000000000000006201277653755400203600ustar00rootroot00000000000000from .meta import AbstractTracePublicCallsMeta, DefaultTraceAbstractMeta, DefaultTraceMeta, TraceAllPublicCallsMeta from .trace_call import trace_call from .tuning import limit_trace_arguments, disable_trace assert AbstractTracePublicCallsMeta assert DefaultTraceAbstractMeta assert DefaultTraceMeta assert TraceAllPublicCallsMeta assert disable_trace assert limit_trace_arguments assert trace_call logfury-0.1.2/src/logfury/v0_1/meta.py000066400000000000000000000104621277653755400175540ustar00rootroot00000000000000from abc import ABCMeta import logging from .trace_call import trace_call class AbstractTraceMeta(type): """ An abstract metaclass for tracing classes """ @classmethod def _filter_attribute(cls, attribute_name, attribute_value): """ decides whether the given attribute should be excluded from tracing or not """ if attribute_name == '__module__': return True elif hasattr(attribute_value, '_trace_disable'): return True return False def __new__(cls, name, bases, attrs, **kwargs): # *magic*: an educated guess is made on how the module that the # processed class is created in would get its logger. # It is assumed that the popular convention recommended by the # developers of standard library (`logger = logging.getLogger(__name__)`) # is used. target_logger = logging.getLogger(attrs['__module__']) for attribute_name in attrs: attribute_value = attrs[attribute_name] if cls._filter_attribute(attribute_name, attribute_value): continue # attrs['__module__'] + '.' + attribute_name is worth logging #assert callable(attribute_value), 'tracing non-methods is not implemented %s %s' % (attribute_name, attribute_value) # collect the `only` and `skip` sets from mro only = getattr(attribute_value, '_trace_only', None) skip = getattr(attribute_value, '_trace_skip', None) disable = False for base in bases: base_attribute_value = getattr(base, attribute_name, None) if base_attribute_value is None: continue # the base class did not define this if hasattr(base_attribute_value, '_trace_disable'): # that's probably done by @disable_trace # ex. inheriting from Abstract class, where getters are marked disable = True break only_candidates = getattr(base_attribute_value, '_trace_only', None) if only_candidates is not None: if only is not None: only.update(only_candidates) else: only = set(only_candidates) skip_candidates = getattr(base_attribute_value, '_trace_skip', None) # is this 5 LOC clone worth refactoring? if skip_candidates is not None: if skip is not None: skip.update(skip_candidates) else: skip = set(skip_candidates) if disable: continue # the base class does not wish to trace it at all # create a wrapper (decorator object) wrapper = trace_call( target_logger, only=only, skip=skip, ) # wrap the callable in it wrapped_value = wrapper(attribute_value) # and substitute the trace-wrapped method for the original attrs[attribute_name] = wrapped_value return super(AbstractTraceMeta, cls).__new__(cls, name, bases, attrs) class TraceAllPublicCallsMeta(AbstractTraceMeta): """ traces all public method calls """ @classmethod def _filter_attribute(cls, attribute_name, attribute_value): if super(TraceAllPublicCallsMeta, cls)._filter_attribute(attribute_name, attribute_value): return True elif not callable(attribute_value): return True # it is a field elif attribute_name.startswith('_'): return True # it is a _protected or a __private method (or __magic__) return False class AbstractTracePublicCallsMeta(ABCMeta, TraceAllPublicCallsMeta): pass class DefaultTraceMeta(TraceAllPublicCallsMeta): """ traces all public method calls, except for ones with names that begin with 'get_' """ @classmethod def _filter_attribute(cls, attribute_name, attribute_value): if super(DefaultTraceMeta, cls)._filter_attribute(attribute_name, attribute_value): return True elif attribute_name.startswith('get_'): return True return False class DefaultTraceAbstractMeta(ABCMeta, DefaultTraceMeta): pass logfury-0.1.2/src/logfury/v0_1/trace_call.py000066400000000000000000000061571277653755400207250ustar00rootroot00000000000000from __future__ import print_function from functools import wraps import logging import six from .utils import get_class_that_defined_method try: from inspect import signature except ImportError: from funcsigs import signature class trace_call(object): """ A decorator which causes the function execution to be logged using a passed logger """ LEVEL = logging.DEBUG def __init__(self, logger, only=None, skip=None): """ only - if not None, contains a whitelist (tuple of names) of arguments that are safe to be logged. All others can not be logged. skip - if not None, contains a whitelist (tuple of names) of arguments that are not safe to be logged. """ self.logger = logger self.only = only self.skip = skip def __call__(self, function): @wraps(function) def wrapper(*wrapee_args, **wrapee_kwargs): if self.logger.isEnabledFor(self.LEVEL): sig = signature(function) bound = sig.bind(*wrapee_args, **wrapee_kwargs) for param in sig.parameters.values(): if param.name not in bound.arguments: bound.arguments[param.name] = param.default args_dict = bound.arguments # filter arguments output_arg_names = [] skipped_arg_names = [] if self.skip is not None and self.only is not None: for arg in six.iterkeys(args_dict): if arg in self.only and arg not in self.skip: output_arg_names.append(arg) else: skipped_arg_names.append(arg) elif self.only is not None: for arg in six.iterkeys(args_dict): if arg in self.only: output_arg_names.append(arg) else: skipped_arg_names.append(arg) elif self.skip is not None: for arg in six.iterkeys(args_dict): if arg in self.skip: skipped_arg_names.append(arg) else: output_arg_names.append(arg) else: output_arg_names = args_dict # format output suffix = '' if skipped_arg_names: suffix = ' (hidden args: %s)' % (', '.join(skipped_arg_names)) arguments = ', '.join('%s=%s' % (k, repr(args_dict[k])) for k in output_arg_names) function_name = function.__name__ klass = get_class_that_defined_method(function) if klass is not None: function_name = '%s.%s' % (klass.__name__, function_name) # actually log the call self.logger.log(self.LEVEL, 'calling %s(%s)%s', function_name, arguments, suffix) return function(*wrapee_args, **wrapee_kwargs) return wrapper logfury-0.1.2/src/logfury/v0_1/tuning.py000066400000000000000000000015271277653755400201340ustar00rootroot00000000000000class limit_trace_arguments(object): """ A decorator which causes the function execution logging to omit some fields """ def __init__(self, only=None, skip=None): """ only - if not None, contains a whitelist (tuple of names) of arguments that are safe to be logged. All others can not be logged. skip - if not None, contains a whitelist (tuple of names) of arguments that are not safe to be logged. """ self.only = only self.skip = skip def __call__(self, function): function._trace_only = self.only function._trace_skip = self.skip return function def disable_trace(function): """ A decorator which suppresses the function execution logging """ function._trace_disable = True return function logfury-0.1.2/src/logfury/v0_1/utils.py000066400000000000000000000014621277653755400177660ustar00rootroot00000000000000import inspect def get_class_that_defined_method(meth): if inspect.ismethod(meth): for cls in inspect.getmro(meth.__self__.__class__): if cls.__dict__.get(meth.__name__) is meth: return cls meth = meth.__func__ # fallback to __qualname__ parsing if inspect.isfunction(meth): if not hasattr(meth, '__qualname__'): pass # python too old else: try: cls = getattr( inspect.getmodule(meth), meth.__qualname__.split('.', 1)[0].rsplit('.', 1)[0] ) # yapf: disable except AttributeError: # defined in an exec() on new python? cls = 'exec' if isinstance(cls, type): return cls return None logfury-0.1.2/test/000077500000000000000000000000001277653755400142055ustar00rootroot00000000000000logfury-0.1.2/test/__init__.py000066400000000000000000000000001277653755400163040ustar00rootroot00000000000000logfury-0.1.2/test/v0_1/000077500000000000000000000000001277653755400147525ustar00rootroot00000000000000logfury-0.1.2/test/v0_1/__init__.py000066400000000000000000000000001277653755400170510ustar00rootroot00000000000000logfury-0.1.2/test/v0_1/test_abstract_meta.py000066400000000000000000000045551277653755400212050ustar00rootroot00000000000000from abc import abstractmethod import six from testfixtures import LogCapture from logfury.v0_1 import AbstractTracePublicCallsMeta, DefaultTraceAbstractMeta from .test_base import TestBase class TestTraceAllPublicCallsMeta(TestBase): def test_subclass(self): @six.add_metaclass(AbstractTracePublicCallsMeta) class Supp(object): @abstractmethod def a(self): pass def __repr__(self): return '<%s object>' % (self.__class__.__name__,) class Ala(Supp): def a(self): pass def bar(self, a, b, c=None): return True a = Ala() a.bar(1, 2, 3) a.bar(1, b=2) with LogCapture() as l: a = Ala() a.bar(1, 2, 3) a.bar(1, b=2) l.check( (__name__, 'DEBUG', 'calling %sbar(self=, a=1, b=2, c=3)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(self=, a=1, b=2, c=None)' % (self._get_prefix(),)), ) class Bela(Supp): # did not define a() def bar(self, a, b, c=None): return True with self.assertRaises(TypeError): Bela() class TestDefaultTraceAbstractMeta(TestTraceAllPublicCallsMeta): def test_subclass(self): @six.add_metaclass(DefaultTraceAbstractMeta) class Supp(object): @abstractmethod def a(self): pass def __repr__(self): return '<%s object>' % (self.__class__.__name__,) class Ala(Supp): def a(self): pass def bar(self, a, b, c=None): return True a = Ala() a.bar(1, 2, 3) a.bar(1, b=2) with LogCapture() as l: a = Ala() a.bar(1, 2, 3) a.bar(1, b=2) l.check( (__name__, 'DEBUG', 'calling %sbar(self=, a=1, b=2, c=3)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(self=, a=1, b=2, c=None)' % (self._get_prefix(),)), ) class Bela(Supp): # did not define a() def bar(self, a, b, c=None): return True with self.assertRaises(TypeError): Bela() logfury-0.1.2/test/v0_1/test_base.py000066400000000000000000000012221277653755400172720ustar00rootroot00000000000000from __future__ import print_function from contextlib import contextmanager import logging import platform import six import unittest class TestBase(unittest.TestCase): def setUp(self): self.logger = logging.getLogger(__name__) self.base_logger_name = __name__ def _get_prefix(self): if six.PY2 or platform.python_implementation() == 'PyPy': return '' else: return self.__class__.__name__ + '.' @contextmanager def assertRaises(self, exc): try: yield except exc: pass else: assert False, 'should have thrown %s' % (exc,) logfury-0.1.2/test/v0_1/test_meta.py000066400000000000000000000144771277653755400173260ustar00rootroot00000000000000import six from testfixtures import LogCapture from logfury.v0_1 import TraceAllPublicCallsMeta, limit_trace_arguments, disable_trace from .test_base import TestBase class TestTraceAllPublicCallsMeta(TestBase): def test_subclass(self): @six.add_metaclass(TraceAllPublicCallsMeta) class Ala(object): def bar(self, a, b, c=None): return True def __repr__(self): return '<%s object>' % (self.__class__.__name__,) class Bela(Ala): def bar(self, a, b, c=None): return False with LogCapture() as l: a = Ala() a.bar(1, 2, 3) a.bar(1, b=2) l.check( (__name__, 'DEBUG', 'calling %sbar(self=, a=1, b=2, c=3)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(self=, a=1, b=2, c=None)' % (self._get_prefix(),)), ) with LogCapture() as l: b = Bela() b.bar(1, 2, 3) b.bar(1, b=2) l.check( (__name__, 'DEBUG', 'calling %sbar(self=, a=1, b=2, c=3)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(self=, a=1, b=2, c=None)' % (self._get_prefix(),)), ) def test_disable_trace(self): @six.add_metaclass(TraceAllPublicCallsMeta) class Ala(object): @disable_trace def bar(self, a, b, c=None): return True def __repr__(self): return '<%s object>' % (self.__class__.__name__,) class Bela(Ala): def bar(self, a, b, c=None): return False with LogCapture() as l: a = Ala() a.bar(1, 2, 3) a.bar(1, b=2) b = Bela() b.bar(1, 2, 3) b.bar(1, b=2) l.check() def test_empty_only(self): @six.add_metaclass(TraceAllPublicCallsMeta) class Ala(object): @limit_trace_arguments(only=tuple()) def bar(self, a, b, c=None): return True def __repr__(self): return '<%s object>' % (self.__class__.__name__,) class Bela(Ala): def bar(self, a, b, c=None): return False with LogCapture() as l: a = Ala() a.bar(1, 2, 3) a.bar(1, b=2) b = Bela() b.bar(1, 2, 3) b.bar(1, b=2) l.check( (__name__, 'DEBUG', 'calling %sbar() (hidden args: self, a, b, c)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar() (hidden args: self, a, b, c)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar() (hidden args: self, a, b, c)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar() (hidden args: self, a, b, c)' % (self._get_prefix(),)), ) def test_skip(self): @six.add_metaclass(TraceAllPublicCallsMeta) class Ala(object): @limit_trace_arguments(skip=['a']) def bar(self, a, b, c=None): return True def __repr__(self): return '<%s object>' % (self.__class__.__name__,) class Bela(Ala): def bar(self, a, b, c=None): return False with LogCapture() as l: a = Ala() a.bar(1, 2, 3) a.bar(1, b=2) b = Bela() b.bar(1, 2, 3) b.bar(1, b=2) l.check( (__name__, 'DEBUG', 'calling %sbar(self=, b=2, c=3) (hidden args: a)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(self=, b=2, c=None) (hidden args: a)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(self=, b=2, c=3) (hidden args: a)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(self=, b=2, c=None) (hidden args: a)' % (self._get_prefix(),)), ) def test_only(self): @six.add_metaclass(TraceAllPublicCallsMeta) class Ala(object): #@limit_trace_arguments(only=['self', 'a', 'b'], skip=['a']) @limit_trace_arguments(only=['a']) def bar(self, a, b, c=None): return True def __repr__(self): return '<%s object>' % (self.__class__.__name__,) class Bela(Ala): def bar(self, a, b, c=None): return False with LogCapture() as l: a = Ala() a.bar(1, 2, 3) a.bar(1, b=2) b = Bela() b.bar(1, 2, 3) b.bar(1, b=2) l.check( (__name__, 'DEBUG', 'calling %sbar(a=1) (hidden args: self, b, c)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(a=1) (hidden args: self, b, c)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(a=1) (hidden args: self, b, c)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(a=1) (hidden args: self, b, c)' % (self._get_prefix(),)), ) def test_skip_and_only(self): @six.add_metaclass(TraceAllPublicCallsMeta) class Ala(object): @limit_trace_arguments(only=['self', 'a', 'b'], skip=['a']) def bar(self, a, b, c=None): return True def __repr__(self): return '<%s object>' % (self.__class__.__name__,) class Bela(Ala): def bar(self, a, b, c=None): return False with LogCapture() as l: a = Ala() a.bar(1, 2, 3) a.bar(1, b=2) b = Bela() b.bar(1, 2, 3) b.bar(1, b=2) l.check( (__name__, 'DEBUG', 'calling %sbar(self=, b=2) (hidden args: a, c)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(self=, b=2) (hidden args: a, c)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(self=, b=2) (hidden args: a, c)' % (self._get_prefix(),)), (__name__, 'DEBUG', 'calling %sbar(self=, b=2) (hidden args: a, c)' % (self._get_prefix(),)), ) logfury-0.1.2/test/v0_1/test_trace_call.py000066400000000000000000000045541277653755400204640ustar00rootroot00000000000000from nose import SkipTest import six from testfixtures import LogCapture from logfury.v0_1 import trace_call from .test_base import TestBase try: from inspect import signature INSPECT_MODULE_NAME = 'inspect' except ImportError: INSPECT_MODULE_NAME = 'funcsigs' from funcsigs import signature assert signature class TestTraceCall(TestBase): def test_all_arguments(self): with LogCapture() as l: @trace_call(self.logger) def foo(a, b, c=None): return True foo(1, 2, 3) foo(1, b=2) l.check( (self.base_logger_name, 'DEBUG', 'calling %sfoo(a=1, b=2, c=3)' % (self._get_prefix(),)), (self.base_logger_name, 'DEBUG', 'calling %sfoo(a=1, b=2, c=None)' % (self._get_prefix(),)), ) def test_complex_signature_py2(self): with LogCapture() as l: @trace_call(self.logger) def foo(a, b, c, d, e, g='G', h='H', i='ii', j='jj', *varargs_, **varkwargs_): pass foo('a', 'b', *['c', 'd'], e='E', Z='Z', **{'g': 'g', 'h': 'h'}) l.check( ( 'test.v0_1.test_base', 'DEBUG', "calling %sfoo(a='a', b='b', c='c', d='d', e='E', " "g='g', h='h', varkwargs_={'Z': 'Z'}, " "i='ii', j='jj', varargs_=)" % (self._get_prefix(), INSPECT_MODULE_NAME) ), ) def test_complex_signature_py3(self): if six.PY2: raise SkipTest() with LogCapture() as l: # without this exec Python 2 and pyflakes complain about syntax errors etc exec ( """@trace_call(self.logger) def foo(a, b, c, d, e, *varargs_, f=None, g='G', h='H', i='ii', j='jj', **varkwargs_: None): pass foo('a', 'b', *['c', 'd'], e='E', f='F', Z='Z', **{'g':'g', 'h':'h'}) """, locals(), globals() ) l.check( ( 'test.v0_1.test_base', 'DEBUG', "calling foo(a='a', b='b', c='c', d='d', e='E', f='F', " "g='g', h='h', varkwargs_={'Z': 'Z'}, varargs_=, " "i='ii', j='jj')" % (INSPECT_MODULE_NAME,) # prefix does not work because of the eval, inspect module is for pypy3 ), )