[
  {
    "path": ".appveyor.yml",
    "content": "build: off\n\nenvironment:\n  matrix:\n    - PYTHON: \"C:\\\\Python27-x64\"\n      PYTHON_VERSION: \"2.7.x\"\n      PYTHON_ARCH: \"64\"\n      TOXENV: \"py27\"\n\n    - PYTHON: \"C:\\\\Python35-x64\"\n      PYTHON_VERSION: \"3.5.x\"\n      PYTHON_ARCH: \"64\"\n      TOXENV: \"py35\"\n\n    - PYTHON: \"C:\\\\Python36-x64\"\n      PYTHON_VERSION: \"3.6.x\"\n      PYTHON_ARCH: \"64\"\n      TOXENV: \"py36\"\n\n    - PYTHON: \"C:\\\\Python37-x64\"\n      PYTHON_VERSION: \"3.7.x\"\n      PYTHON_ARCH: \"64\"\n      TOXENV: \"py37\"\n\n    - PYTHON: \"C:\\\\Python38-x64\"\n      PYTHON_VERSION: \"3.8.x\"\n      PYTHON_ARCH: \"64\"\n      TOXENV: \"py38\"\n\ninstall:\n  - \"SET PATH=%PYTHON%;%PYTHON%\\\\Scripts;%PATH%\"\n  - \"%CMD_IN_ENV% pip install tox codecov\"\n\ntest_script:\n  - \"%CMD_IN_ENV% tox\"\n\non_success:\n  - \"%CMD_IN_ENV% codecov\"\n"
  },
  {
    "path": ".coveragerc",
    "content": "[run]\nbranch = True\nsource = pydu\n\n[paths]\nsource =\n   pydu\n   .tox/*/lib/python*/site-packages/pydu"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# IDE\n.idea\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n.hypothesis/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# pyenv\n.python-version\n\n# celery beat schedule file\ncelerybeat-schedule\n\n# SageMath parsed files\n*.sage.py\n\n# dotenv\n.env\n\n# virtualenv\n.venv\nvenv/\nENV/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n\n# pytest\n.pytest_cache/\n\n# Mac\n\n.DS_Store\n\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: python\npython:\n  - \"2.7\"\n  - \"3.5\"\n  - \"3.6\"\n  - \"3.7\"\n  - \"3.8\"\n\nsudo: false\n\ncache: pip\n\ninstall:\n  - pip install tox codecov\n\nscript:\n  - tox -e $(echo py$TRAVIS_PYTHON_VERSION | tr -d .)\n\nafter_success:\n  - codecov\n\nnotifications:\n  email:\n    recipients:\n      - wangbinxin001@126.com\n    on_success: always\n    on_failure: always\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "v0.7.2 (2019-02-08)\n-------------------\n\n**Bug fixes**\n\n* Fix collections ABCs deprecation warning\n\n\nv0.7.0 (2018-05-14)\n-------------------\n\n**Enhancements**\n\n* \bUpgrade to **brand new document** powerd by docsify\n* Add slot.SlotBase which is the base class for class using `__slots__`\n* Add compat.izip\n\n\nv0.6.2 (2018-04-30)\n-------------------\n\n**Enhancements**\n\n* Add ``exception.default_if_except`` which excepts given exceptions and return default value as decorator.\n\n\nv0.6.1 (2018-04-23)\n-------------------\n\n**Enhancements**\n\n* Add ``dt.timer`` which can time how long does calling take as a context manager or decorator.\n\n\nv0.6.0 (2018-04-16)\n-------------------\n\n**Enhancements**\n\n* Add ``path.filename`` which return the filename without extension.\n* Add ``path.fileext`` which return the file extension.\n* Update stub for ``requests.check_connect``.\n\n\nv0.5.2 (2018-04-04)\n-------------------\n\n**Enhancements**\n\n* Add ``system.preferredencoding`` which gets best encoding for the system.\n* Add ``request.update_query_params`` which update query params of given url and return new url.\n* Update stub for ``requests.check_connect``.\n\n\nv0.5.1 (2018-03-19)\n-------------------\n\n**Enhancements**\n\n* Improve ``system.remove`` when path is read-only.\n* Add ``path.normjoin`` which join one or more path components intelligently and normalize it.\n* Improve ``environ.environ`` with supporting variable_name=None which means removing the variable from environment temporarily.\n\n\nv0.5.0 (2018-03-08)\n-------------------\n\n**Enhancements**\n\n* Add ``network.private_ipv4s`` which stores private IPV4 addresses.\n* Add ``functional.compose`` which composes all functions into one.\n* Add ``TYPE HINT`` for ALL MODULES by supplying STUB FILES!\n\n**Bug fixes**\n\n* Fix reduce error on Python 3.\n\n\nv0.4.2 (2018-02-05)\n-------------------\n\n**Enhancements**\n\n* Add ``socket.inet_pton`` and ``socket.inetntop`` for Windows if we ``import pydu.network``.\n* Add ``network.ip2int`` and ``network.int2ip`` which convert ip to integer or integer to ip.\n* Add ``process.get_processes_by_path`` for getting processes which are running on given path or sub path of given path.\n* Add ``first``, ``last``, ``all``, ``any`` and ``join`` to ``pydu.iter``, which support many operations on iterable object.\n\n**Bug fixes**\n\n* Fix several convert functions return values with unnecessary value 'L' when given big number on Python 2.\n\n\nv0.4.1 (2018-01-20)\n-------------------\n\n**Enhancements**\n\n* Add ``bin2oct``, ``bin2dec``, ``bin2hex``, ``oct2bin``, ``oct2dec``, ``oct2hex``, ``dec2bin``, ``dec2oct``, ``dec2hex``, ``hex2bin``, ``hex2oct``, ``hex2dec`` to ``convert``, which support many base conversions\n* Add ``path.is_super_path`` which judges whether the given ``path1`` is the super path of ``path2``\n* Add ``environ.environ`` which is a context manager for updating one or more environment variables\n* Add ``environ.path`` which is a context manager for updating the PATH environment variable\n* Add ``list.tolist`` which converts obj to list\n* Add ``list.flatten`` which generates each element of the given ``seq``\n* Add ``compat.strbytes_types`` which includes all types about string\n\n\nv0.4.0 (2018-01-09)\n-------------------\n\n**Importance**\n* Remove support for Python 3.4\n\n**Enhancements**\n\n* Add ``dict.OrderedDefaultDict`` which remembers insertion order and has default value with default factory\n* Add ``convert.boolean`` which converts obj to a boolean value\n* ``console.console_size`` will use ``shutil.get_terminal_size`` if possible\n* ``exception.ignore`` is same to ``context.lib.suppress`` on Python 3\n\n**Bug fixes**\n\n* Fix #15 (If the ``dict.attrify``'s obj is tuple, this will raise a error)\n\n\nv0.3.1 (2017-12-29)\n-------------------\n\n**Enhancements**\n\n* Add ``FileTracker`` which could track opening files.\n\n\n**Bug fixes**\n\n* Fix ``pip install`` error on Windows with Python 3.\n* Fix ``network.is_ipv6`` test error on Windows with Python 3.\n* Fix description error on ``network``, ``request`` doc.\n\n\nv0.3.0 (2017-12-26)\n-------------------\n\n**Enhancements**\n\n* Rename ``file`` to ``system``.\n* Add ``system.which`` which supports find executable file.\n* Add ``system.chmod`` which supports chmod recursively.\n* Add ``unit.Bytes`` which used to deal with bytes.\n* Add ``preferredencoding`` to ``string``.\n* Add ``cmd.chcp`` for Windows which is same like ``chcp`` on Windows cmd.\n* Add ``cmd.run_with_en_env`` which ensure the output of cmd is in English.\n* Add ``cmd.terminate`` which supports terminate process by given ``pid``.\n* ``cmd.run`` uses timeout feature on Python 3 but not implement by self.\n\n\n**Bug fixes**\n\n* Fix test cases to generate right coverage.\n\n\nv0.2.0 (2017-12-17)\n-------------------\n\n**Enhancements**\n\n* Add ``exception.ignore``.\n* ``network.is_ipv6`` is available on Windows.\n* Set logging handler to avoid \"No handler found\" warnings.\n* Add ``Makefile`` which make development easier.\n* Update ``readme`` which is more readable.\n\n**Bug fixes**\n\n* Fix installation error on Windows.\n\n\nv0.1.0 (2017-12-14)\n-------------------\n\nSupply many powerful data structures and utils about archive, cmd, compat, console, dict, file, inspect, list, misc, network, path, platform, request, set and string."
  },
  {
    "path": "LICENSE.txt",
    "content": "MIT License\n\nCopyright (c) 2017 Prodesire\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include requirements-dev.txt README.md CHANGELOG.md LICENSE.txt"
  },
  {
    "path": "Makefile",
    "content": "# Env\nexport PYTHONDONTWRITEBYTECODE=1\nTEST_PATH=./tests\nDEFAULT_PYTHON2=`python -c \"import sys;print(sys.version_info.major)\" | grep 2`\nPY2=$(if $(DEFAULT_PYTHON2),python,python2)\nPY3=$(if $(DEFAULT_PYTHON2),python3,python)\n\n# Func\n.PHONY: docs\n\nhelp:\n\t@echo \"\\033[32minit\\033[0m\"\n\t@echo \"    Init environment for pydu.\"\n\t@echo \"\\033[32mtest\\033[0m\"\n\t@echo \"    Run pytest with Python 2 and 3.\"\n\t@echo \"\\033[32mtest-py2\\033[0m\"\n\t@echo \"    Run pytest with Python 2.\"\n\t@echo \"\\033[32mtest-py3\\033[0m\"\n\t@echo \"    Run pytest with Python 3.\"\n\t@echo \"\\033[32mcoverage\\033[0m\"\n\t@echo \"    Run pytest and report coverage.\"\n\t@echo \"\\033[32mpublish\\033[0m\"\n\t@echo \"    Publish pydu to PyPI.\"\n\t@echo \"\\033[32mdocs\\033[0m\"\n\t@echo \"    Make docs for pydu.\"\n\t@echo \"\\033[32mclean\\033[0m\"\n\t@echo \"    Remove python and build artifacts.\"\n\t@echo \"\\033[32mclean-pyc\\033[0m\"\n\t@echo \"    Remove python artifacts.\"\n\t@echo \"\\033[32mclean-build\\033[0m\"\n\t@echo \"    Remove build artifacts.\"\n\ninit:\n\tpip install -r requirements-dev.txt\n\tnpm i docsify-cli -g\n\ntest: test-py2 test-py3\n\ntest-py2: clean-pyc\n\t $(PY2) -m pytest --color=yes $(TEST_PATH)\n\ntest-py3: clean-pyc\n\t $(PY3) -m pytest --color=yes $(TEST_PATH)\n\ncoverage:\n\tcoverage run --source=pydu -m pytest tests\n\tcoverage report\n\npublish:\n\tpip install 'twine>=1.5.0'\n\tpython setup.py sdist\n\ttwine upload dist/*\n\trm -rf build dist *.egg-info .eggs\n\ndocs:\n\tdocsify serve docs\n\nclean: clean-pyc clean-build\n\nclean-pyc:\n\tfind . -name '*.pyc' -exec rm -f {} +\n\tfind . -name '*.pyo' -exec rm -f {} +\n\tfind . -name '*~' -exec rm -f  {} +\n\tfind . -name '__pycache__' -exec rm -rf {} +\n\nclean-build:\n\trm -rf build dist *.egg-info .eggs\n"
  },
  {
    "path": "README.md",
    "content": "# pydu\n\n[![pydu](https://img.shields.io/pypi/v/pydu.svg)](https://pypi.python.org/pypi/pydu)\n[![pydu](https://img.shields.io/pypi/l/pydu.svg)](https://pypi.python.org/pypi/pydu)\n[![pydu](https://img.shields.io/pypi/pyversions/pydu.svg)](https://pypi.python.org/pypi/pydu)\n[![pydu](https://img.shields.io/travis/flaggo/pydu/master.svg?label=Linux)](https://travis-ci.org/flaggo/pydu)\n[![pydu](https://img.shields.io/appveyor/ci/flaggo/pydu/master.svg?label=Windows)](https://ci.appveyor.com/project/flaggo/pydu)\n[![pydu](https://codecov.io/github/flaggo/pydu/coverage.svg?branch=master)](https://codecov.io/github/flaggo/pydu)\n[![pydu](https://img.shields.io/github/contributors/flaggo/pydu.svg)](https://github.com/flaggo/pydu/graphs/contributors)\n\n**pydu** is a library of useful **d**ata structures and **u**tils\nfor Python 2 and 3, which collected from open source projects and created by contributors.\n\n\n## Installation\n\nTo install pydu, simply:\n\n```bash\n$ pip install pydu\n```\n\n## Document\n\nFantastic documentation is available at: [English](https://flaggo.github.io/pydu/) | [中文版](https://flaggo.github.io/pydu/#/zh-cn/).\n"
  },
  {
    "path": "docs/.nojekyll",
    "content": ""
  },
  {
    "path": "docs/README.md",
    "content": "## pydu\n\n> **pydu(Python Data structures and Utils)** is a library of useful data structures and utils\nfor Python 2 and 3, which collected from open source projects and created by contributors.\n\n\n## Installation\nTo install **pydu**, simply:\n\n```bash\n$ pip install pydu\n```\n"
  },
  {
    "path": "docs/_coverpage.md",
    "content": "# pydu <small>0.7.0</small>\n\n> Python Data structures and Utils.\n\n* Rich basic data structures\n* A variety of utils for handling different situations\n\n[GitHub](https://github.com/flaggo/pydu/)\n[Modules](#pydu)\n"
  },
  {
    "path": "docs/_navbar.md",
    "content": "* [En](/)\n* [中文](/zh-cn/)\n"
  },
  {
    "path": "docs/_sidebar.md",
    "content": "* Modules\n\n  * [Archive](archive.md)\n  * [Commad](cmd.md)\n  * [Compat](compat.md)\n  * [Console](console.md)\n  * [Convert](convert.md)\n  * [Dict](dict.md)\n  * [Date and Time](dt.md)\n  * [Environment](environ.md)\n  * [Exception](exception.md)\n  * [Functional](functional.md)\n  * [Inspect](inspect.md)\n  * [Iter](iter.md)\n  * [List](list.md)\n  * [Miscellanea](misc.md)\n  * [Network](network.md)\n  * [Path](path.md)\n  * [Platform](platform.md)\n  * [Process](process.md)\n  * [Request](request.md)\n  * [Set](set.md)\n  * [Slot](slot.md)\n  * [String](string.md)\n  * [System](system.md)\n  * [Unit](unit.md)\n\n* [Changelog](changelog.md)\n"
  },
  {
    "path": "docs/archive.md",
    "content": "# archive\n\nUtils for archiving files.\n\n## archive.extract\n```python\nextract(path, to_path='', ext='')\n```\n\nUnpack the tar or zip file at the specified path or file to the directory\nspecified by ``to_path``. It supports many extensions, like ``.tar``,\n``.tar.bz2``, ``.tar.gz``, ``.tgz``, ``.tz2``, ``.zip``. If the file name of\ngiven ``path`` doesn't contain file extension, the ``ext`` parameter can be\nspecified one of supported extensions to indicate file type.\n\n```python\n>>> from pydu.archive import extract\n>>> extract('foobar.tgz', '/tmp')\n>>> extract('foobar', '/tmp', ext='.tgz')\n>>> extract('foobar', '/tmp')\nTraceback (most recent call last):\n  ...    AttributeError: pydu.archive.UnrecognizedArchiveFormat: Path not a recognized archive format: foobar\n```\n"
  },
  {
    "path": "docs/cmd.md",
    "content": "# cmd\n\nUtils for running command and getting command line.\n\n## cmd.TimeoutExpired\n```python\nTimeoutExpired(cmd, timeout, output=None, stderr=None)\n```\n\nThis exception is raised when the timeout expires while waiting for a\nchild process.\n\nAttributes:\n    cmd, output, stdout, stderr, timeout\n\n\n## cmd.run\n```python\nrun(cmd, shell=False, env=None, timeout=None, timeinterval=1)\n```\n\nRun cmd based on `subprocess.Popen` and return the tuple of ``(returncode, stdout)``.\n\nNote, `stderr` is redirected to `stdout`. `shell` is same to parameter of `Popen`.\n\nIf the process does not terminate after `timeout` seconds, a `TimeoutExpired` exception will be raised.\n`timeinterval` is workable when timeout is given on Python 2. It means process status checking interval.\n\nThe child process is not killed if the timeout expires, so in order to cleanup properly a well-behaved application should kill the child process and finish communication.\n\n```python\n>>> from pydu.cmd import run\n>>> run('echo hello')\n(0, b'hello\\r\\n')  # Python 3\n```\n\n\n## cmd.run_with_en_env\n```python\nrun_with_en_env(cmd, shell=False, env=None, timeout=None, timeinterval=1)\n```\n\nRun cmd with English character sets environment, so that the output will\nbe in English.\nParameters are same with `run`.\n\n\n## cmd.terminate\n```python\nterminate(pid)\n```\n\nTerminate process by given `pid`.\n\nOn Windows, using `kernel32.TerminateProcess` to kill.\nOn other platforms, using `os.kill` with `signal.SIGTERM` to kill.\n\n\n## cmd.cmdline_argv\n```python\ncmdline_argv()\n```\n\nGet command line argv of self python process. On Windows when using Python 2,\n`cmdline_argv` is implemented by using `shell32.GetCommandLineArgvW` to get\n`sys.argv` as a list of Unicode strings.\n\nOn other platforms or using Python 3, `cmdline_argv` is same to `sys.argv`.\n\n```python\n>>> from pydu.cmd import cmdline_argv\n>>> cmdline_argv()\n['/Applications/PyCharm.app/Contents/helpers/pydev/pydevconsole.py', '61253', '61254']\n```\n"
  },
  {
    "path": "docs/compat.md",
    "content": "# compat\n\ncompatible data structures, libs, functions for Python 2 and 3.\n\n## compat.PY2\n\nSpecify current Python interpreter is Python 2 or 3.\n\n\n## compat.urlib\n```python\nurlib(base, url, allow_fragments=True)\n```\n\nSame to ``urllib`` on PY2 or ``urllib.request`` on PY3.\n\n\n## compat.urlparse\n```python\nurlparse(base, url, allow_fragments=True)\n```\n\nSame to ``urlparse`` on PY2 or ``urllib.parse`` on PY3.\n\n\n## compat.urljoin\n```python\nurljoin(base, url, allow_fragments=True)\n```\n\nSame to ``urlparse.urljoin`` on PY2 or ``urllib.parse.urljoin`` on PY3.\n\n\n## compat.iterkeys\n```python\niterkeys(d)\n```\n\nReturn an iter object of dictionary keys.\n\n\n## compat.itervalues\n```python\nitervalues(d)\n```\n\nReturn an iter object of dictionary values.\n\n\n## compat.iteritems\n```python\niteritems(d)\n```\n\nReturn an iter object of dictionary items.\n\n\n## compat.text_type\n\nThe text type is ``unicode`` on PY2 or ``str`` on PY3.\n\n\n## compat.string_types\n\nThe string types are ``(str, unicode)`` on PY2 or ``(str,)`` on PY3.\n\n## compat.strbytes_types\n\nThe strbytes(string bytes) types are ``(str, unicode, bytes)`` on PY2 or ``(str, bytes)`` on PY3.\n\n\n## compat.numeric_types\n\nThe numeric types are ``(int, long)`` on PY2 or ``(int,)`` on PY3.\n\n\n## compat.imap\n```python\nimap(func, *iterables)\n```\n\nSame to ``itertools.imap`` on PY2 or ``map`` on PY3.\n\n\n## compat.izip\n```python\nizip(iter1 [,iter2 [...])\n```\n\nSame to ``itertools.izip`` on PY2 or ``zip`` on PY3.\n\n\n## compat.reduce\n```python\nreduce(function, sequence, initial=None)\n```\n\nSame to built-in ``reduce`` on PY2 or ``functools.reduce`` on PY3.\n\n\n## compat.cmp\n```python\ncmp(x, y)\n```\n\nSame to ``cmp`` on PY2, but implement on PY3.\n\n\n## compat.has_next_attr\n```python\nhas_next_attr(x)\n```\n\nAn implementation independent way of checking for next attribute.\n\n\n## compat.is_iterable\n```python\nis_iterable(x)\n```\n\nAn implementation independent way of checking for iterables.\n\n```python\n>>> from pydu.compat import is_iterable\n>>> is_iterable([])\nTrue\n>>> is_iterable(1)\nFalse\n```\n"
  },
  {
    "path": "docs/console.md",
    "content": "# Console\n\nUtils for handling console.\n\n## console.console_size\n```python\nconsole_size(fallback=(80, 25))\n```\n\nFor Windows, return (width, height) of available window area, fallback\nif no console is allocated.\nFor POSIX system, return (width, height) of console terminal, fallback\non IOError, i.e. when no console is allocated.\nFor other system, return fallback.\nFallback defaults to (80, 25) which is the default size used by many\nterminal emulators.\n\n```python\n>>> from pydu.console import console_size\n>>> console_size()\n(80, 25)\n```\n"
  },
  {
    "path": "docs/convert.md",
    "content": "# Convert\n\nUtils for converting one type of data to another.\n\n\n## convert.boolean\n```python\nboolean(obj)\n```\n\nConvert obj to a boolean value.\n\nIf obj is string, obj will converted by case-insensitive way:\n\n* convert `yes`, `y`, `on`, `true`, `t`, `1` to True\n* convert `no`, `n`, `off`, `false`, `f`, `0` to False\n* raising TypeError if other values passed\n\nIf obj is non-string, obj will converted by `bool(obj)`.\n\n```python\n>>> from pydu.string import boolean\n>>> boolean('yes')\nTrue\n>>> boolean('no')\nFalse\n```\n\n\n## convert.bin2oct\n```python\nbin2oct(x)\n```\n\nConvert binary string to octal string.\nFor instance: '1001' -> '11'\n\n```python\n>>> from pydu.convert import bin2oct\n>>> bin2oct('1001')\n'11'\n```\n\n\n## convert.bin2dec\n```python\nbin2dec(x)\n```\n\nConvert binary string to decimal number.\nFor instance: '11' -> 3\n\n```python\n>>> from pydu.convert import bin2dec\n>>> bin2dec('11')\n3\n```\n\n\n## convert.bin2hex\n```python\nbin2hex(x)\n```\n\nConvert binary string to hexadecimal string.\nFor instance: '11010' -> '1a'\n\n```python\n>>> from pydu.convert import bin2hex\n>>> bin2hex('11010')\n'1a'\n```\n\n\n## convert.oct2bin\n```python\noct2bin(x)\n```\n\nConvert octal string to binary string.\nFor instance: '11' -> '1001'\n\n```python\n>>> from pydu.convert import oct2bin\n>>> oct2bin('11')\n'1001'\n```\n\n\n## convert.oct2dec\n```python\noct2dec(x)\n```\n\nConvert octal string to decimal number.\nFor instance: '11' -> 9\n\n```python\n>>> from pydu.convert import oct2dec\n>>> oct2dec('11')\n9\n```\n\n\n## convert.oct2hex\n```python\noct2hex(x)\n```\n\nConvert octal string to hexadecimal string.\nFor instance: '32' -> '1a'\n\n```python\n>>> from pydu.convert import oct2hex\n>>> oct2hex('32')\n'1a'\n```\n\n\n## convert.dec2bin\n```python\ndec2bin(x)\n```\n\nConvert decimal number to binary string.\nFor instance: 3 -> '11'\n\n```python\n>>> from pydu.convert import dec2bin\n>>> dec2bin(3)\n'11'\n```\n\n\n## convert.dec2oct\n```python\ndec2oct(x)\n```\n\nConvert decimal number to octal string.\nFor instance: 9 -> '11'\n\n```python\n>>> from pydu.convert import dec2oct\n>>> dec2oct(9)\n'11'\n```\n\n\n## convert.dec2hex\n```python\ndec2hex(x)\n```\n\nConvert decimal number to hexadecimal string.\nFor instance: 26 -> '1a'\n\n```python\n>>> from pydu.convert import dec2hex\n>>> dec2hex(26)\n'1a'\n```\n\n\n## convert.hex2bin\n```python\nhex2bin(x)\n```\n\nConvert hexadecimal string to binary string.\nFor instance: '1a' -> '11010'\n\n```python\n>>> from pydu.convert import hex2bin\n>>> hex2bin('1a')\n'11010'\n```\n\n\n## convert.hex2oct\n```python\nhex2oct(x)\n```\n\nConvert hexadecimal string to octal string.\nFor instance: '1a' -> '32'\n\n```python\n>>> from pydu.convert import hex2oct\n>>> hex2oct('1a')\n'32'\n```\n\n\n## convert.hex2dec\n```python\nhex2dec(x)\n```\n\nConvert hexadecimal string to decimal number.\nFor instance: '1a' -> 26\n\n```python\n>>> from pydu.convert import hex2dec\n>>> hex2dec('1a')\n26\n```\n"
  },
  {
    "path": "docs/dict.md",
    "content": "# Dict\n\nAdditional powerful dictionaries and relative functions.\n\n## dict.AttrDict\n```python\nAttrDict(seq=None, **kwargs)\n```\n\nA AttrDict object is like a dictionary except `obj.foo` can be used\nin addition to `obj['foo']`.\n\n```python\n>>> from pydu.dict import AttrDict\n>>> o = AttrDict(a=1)\no.a\n1\n>>> o['a']\n1\n>>> o.a = 2\n>>> o['a']\n2\n>>> del o.a\n>>> o.a\nTraceback (most recent call last):\n...    AttributeError: 'a'\n```\n\n\n## dict.CaseInsensitiveDict\n```python\nCaseInsensitiveDict(data=None, **kwargs)\n```\n\nA case-insensitive `dict`-like object.\nImplements all methods and operations of `collections.MutableMapping`\nas well as dict's `copy`. Also provides `lower_items`.\nAll keys are expected to be strings. The structure remembers the\ncase of the last key to be set, and `iter(instance)`, `keys()`,\n`items()`, `iterkeys()`, and `iteritems()` will contain\ncase-sensitive keys.\n\n```python\n>>> from pydu.dict import CaseInsensitiveDict\n>>> cid = CaseInsensitiveDict()\n>>> cid['Accept'] = 'application/json'\n>>> cid['aCCEPT'] == 'application/json'\nTrue\n>>> list(cid) == ['Accept']\nTrue\n```\n\n\n## dict.LookupDict\n```python\nLookupDict(name=None)\n```\n\nDictionary lookup object.\n\n```python\n>>> from pydu.dict import LookupDict\n>>> d = LookupDict()\n>>> d['key']\nNone\n>>> d['key'] = 1\n>>> d['key']\n1\n```\n\n## dict.OrderedDefaultDict\n```python\nOrderedDefaultDict(default_factory=None, *args, **kwds)\n```\n\nDictionary that remembers insertion order and has default value\nwith default factory.\n\nThe default factory is called without arguments to produce\na new value when a key is not present, in `__getitem__` only.\nAn `OrderedDefaultDict` compares equal to a `collections.defaultdict`\nwith the same items. All remaining arguments are treated the same\nas if they were passed to the `defaultdict` constructor,\nincluding keyword arguments.\n\n```python\n>>> from pydu.dict import OrderedDefaultDict\n>>> d = OrderedDefaultDict(int)\n>>> d['b']\n0\n>>> d['a']\n0\n>>> d.keys()\nodict_keys(['b', 'a'])\n```\n\n\n## dict.attrify\n```python\nattrify(obj)\n```\n\nAttrify obj into `AttriDict` or `list of AttriDict` if the obj is list.\nIf obj or the item of obj is not list or dict, will return itself.\n\n```python\n>>> from pydu.dict import attrify\n>>> attrd = attrify({\n'a': [1, 2, {'b': 'b'}],\n'c': 'c',\n})\n>>> attrd\n<AttrDict {'a': [1, 2, <AttrDict {'b': 'b'}>], 'c': 'c'}>\n>>> attrd.a\n1\n>>> attrd.a[2].b\nb\n>>> attrd.c\nc\n```\n"
  },
  {
    "path": "docs/dt.md",
    "content": "# Date and Time\n\nUtils for handling date and time.\n\n## dt.timer\n```python\ntimer(path)\n```\n\nA timer can time how long does calling take as a context manager or decorator.\nIf assign `print_func` with `sys.stdout.write`, `logger.info` and so on,\ntimer will print the spent time.\n\n```python\ntimeit = timer(print_func=sys.stdout.write)\nwith timeit:\nfoo()\n\n@timeit\ndef foo():\npass\n```\n\n`timer.elapsed` contains the total amount of elapsed\ntime of running `foo`.\n\n```python\n>>> timeit = timer(print_func=sys.stdout.write)\n>>> with timeit:\n...     os.getcwd()\nSpent time: 1.7881393432617188e-05s\n```\n"
  },
  {
    "path": "docs/environ.md",
    "content": "# Environ\n\nUtils for handling environment.\n\n\n## environ.environ\n```python\nenviron(**kwargs)\n```\n\nContext manager for updating one or more environment variables.\n\nPreserves the previous environment variable (if available) and\nrecovers when exiting the context manager.\n\nIf given variable_name=None, it means removing the variable from\nenvironment temporarily.\n\n```python\n>>> from pydu.environ import environ\n>>> with environ(a='a'):\n...     print(os.environ['a'])\n...\na\n```\n\n\n## environ.path\n```python\npath(append=None, prepend=None, replace=None)\n```\n\nContext manager for updating the PATH environment variable which\nappends, prepends or replaces the PATH with given string or\na list of strings.\n\n```python\n>>> import os\n>>> from pydu.environ import path\n>>> with path(append='/foo'):\n...     print(os.environ['PATH'])\n...\n/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/foo\n```\n"
  },
  {
    "path": "docs/exception.md",
    "content": "# Exception\n\nUtils for handling exceptions.\n\n## exception.ignore\n```python\nignore(*exceptions)\n```\n\nA context manager which can ignore given exceptions.\n\n```python\n>>> from pydu.exception import ignore\n>>> with ignore(ValueError, AttributeError):\n...     int('abc')\n...     int.no_exists_func()\n...\n>>>\n```\n\n## exception.default_if_except\n```python\ndefault_if_except(exception_clses, default=None)\n```\n\nA exception decorator which excepts given exceptions and return default value.\n\n```python\n>>> from pydu.exception import default_if_except\n>>> @default_if_except(ValueError, default=0)\n... def foo(value):\n...     return int(value)\n>>> foo('abc')\n0\n```\n"
  },
  {
    "path": "docs/functional.md",
    "content": "# functional\n\nUtils for functional programming.\n\n## functional.compose\n```python\ncompose(*funcs)\n```\n\nCompose all functions. The previous function must accept one argument,\nwhich is the output of the next function. The last function can accept\nany args and kwargs. \n`compose(f1, f2, f3)(*x)` is same to `f1(f2(f3(*x)))`.\n\n```python\n>>> from pydu.functional import compose\n>>> def f1(a):\n...     return a+1\n...\n>>> def f2(a, b=2):\n...     return a+b\n...\n>>> compose(f1, f2)(1, b=3)\n5\n```\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>pydu - Python Data structures and Utils</title>\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\" />\n  <meta name=\"description\" content=\"Description\">\n  <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n  <link rel=\"stylesheet\" href=\"//unpkg.com/docsify/lib/themes/vue.css\">\n</head>\n<body>\n  <div id=\"app\">Loading ...</div>\n  <script>\n    window.$docsify = {\n      name: 'pydu',\n      repo: 'https://github.com/flaggo/pydu',\n      loadSidebar: true,\n      loadNavbar: true,\n      subMaxLevel: 2,\n      coverpage: true,\n      alias: {\n        '.*?/changelog': 'https://raw.githubusercontent.com/flaggo/pydu/master/CHANGELOG.md',\n      },\n      formatUpdated: '{YYYY}-{MM}-{DD} {HH}:{mm}',\n      search: {\n            placeholder: {\n              '/zh-cn/': '搜索',\n              '/': 'Type to search',\n            },\n            noData: {\n              '/zh-cn/': '找不到结果',\n              '/': 'No Results',\n            }\n        },\n        plugins: [\n          function (hook, vm) {\n            hook.beforeEach(function (html) {\n              var url = 'https://github.com/flaggo/pydu/blob/master/docs/' + vm.route.file\n              var editHtml = '[:memo: Edit Document](' + url + ')\\n'\n\n              return editHtml\n                + html\n                + '\\n\\n----\\n\\n'\n                + '<a href=\"https://github.com/flaggo/pydu\">pydu</a> Copyright &copy; 2017-2018 <a href=\"https://github.com/Prodesire\">Prodesire</a>. '\n                + 'Powered By <a href=\"https://github.com/QingWei-Li/docsify\" target=\"_blank\">docsify</a>.'\n            })\n          }\n        ]\n    }\n  </script>\n  <script src=\"//unpkg.com/docsify/lib/docsify.min.js\"></script>\n  <script src=\"//unpkg.com/docsify/lib/plugins/search.js\"></script>\n  <script src=\"//unpkg.com/prismjs/components/prism-python.min.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "docs/inspect.md",
    "content": "# inspect\n\nUtils for inspecting functions.\n\n## inspect.getargspec\n```python\ngetargspec(func)\n```\n\nGet the names and default values of a function's arguments.\n\nA tuple of four things is returned: (args, varargs, varkw, defaults).\n`args` is a list of the argument names (it may contain nested lists).\n`varargs` and `varkw` are the names of the * and ** arguments or None.\n`defaults` is an n-tuple of the default values of the last n arguments.\n\n```python\n>>> from pydu.inspect import getargspec\n>>> def f(name, address='home', age=25, *args, **kwargs):\n...     pass\n...\n>>> getargspect(f)\nArgSpec(args=['name', 'address', 'age'], varargs='args', keywords='kwargs', defaults=('home', 25))\n```\n\n\n## inspect.get_func_args\n```python\nget_func_args(func)\n```\n\nReturn a list of the argument names. Arguments such as\n`*args` and `**kwargs` are not included.\n\n```python\n>>> from pydu.inspect import get_func_args\n>>> def f(name, address='home', age=25, *args, **kwargs):\n...     pass\n...\n>>> get_func_args(f)\n['name', 'address', 'age']\n```\n\n\n## inspect.get_func_full_args\n```python\nget_func_full_args(func)\n```\n\nReturn a list of (argument name, default value) tuples. If the argument\ndoes not have a default value, omit it in the tuple. Arguments such as\n`*args` and `**kwargs` are also included.\n\n```python\n>>> from pydu.inspect import get_func_full_args\n>>> def f(name, address='home', age=25, *args, **kwargs):\n...     pass\n...\n>>> get_func_full_args(f)\n[('name',), ('address', 'home'), ('age', 25), ('*args',), ('**kwargs',)]\n```\n\n\n## inspect.func_accepts_kwargs\n```python\nfunc_accepts_kwargs(func)\n```\n\nCheck whether or not the func accepts kwargs.\n\n```python\n>>> from pydu.inspect import func_accepts_kwargs\n>>> def f(**kwargs):\n...     pass\n...\n>>> func_accepts_kwargs(f)\nTrue\n```\n\n\n## inspect.func_accepts_var_args\n```python\nfunc_accepts_var_args(func)\n```\n\nCheck whether or not the func accepts var args.\n\n```python\n>>> from pydu.inspect import func_accepts_var_args\n>>> def f(*vargs):\n...     pass\n...\n>>> func_accepts_var_args(f)\nTrue\n```\n\n\n## inspect.func_supports_parameter\n```python\nfunc_supports_parameter(func)\n```\n\nCheck whether or the func supports the given parameter.\n\n```python\n>>> from pydu.inspect import func_supports_parameter\n>>> def f(name):\n...     pass\n...\n>>> func_supports_parameter(f, 'name')\nTrue\n>>> func_supports_parameter(f, 'unkown')\nFasle\n```\n\n\n## inspect.func_has_no_args\n```python\nfunc_has_no_args(func)\n```\n\nCheck whether or not the func has any args.\n\n```python\n>>> from pydu.inspect import func_has_no_args\n>>> def f():\n...     pass\n...\n>>> func_has_no_args(f)\nTrue\n```\n"
  },
  {
    "path": "docs/iter.md",
    "content": "# iter\n\nUtils for handling iterations.\n\n## iter.first\n```python\nfirst(iterable)\n```\n\nGet the first item in the iterable.\n\n```python\n>>> from pydu.iter import first\n>>> first([1, 2])\n1\n```\n\n\n## iter.last\n```python\nlast(iterable)\n```\n\nGet the last item in the iterable.\nWarning, this can be slow due to iter step by step to last one.\n\n```python\n>>> from pydu.iter import last\n>>> last([1, 2])\n2\n```\n\n\n## iter.all\n```python\nall(iterable, predicate)\n```\n\nReturns True if all elements in the given iterable are True for the\ngiven predicate function.\n\n```python\n>>> from pydu.iter import all\n>>> all([0, 1, 2], lambda x: x+1)\nTrue\n```\n\n\n## iter.any\n```python\nany(iterable)\n```\n\nReturns True if any element in the given iterable is True for the\ngiven predicate function.\n\n```python\n>>> from pydu.iter import any\n>>> any([-1, -1, 0], lambda x: x+1)\nTrue\n```\n\n\n## iter.join\n```python\njoin(iterable, separator='')\n```\n\nJoin each item of iterable to string.\n\n```python\n>>> from pydu.iter import join\n>>> join([1, '2', 3], separator=',')\n'1,2,3'\n```\n"
  },
  {
    "path": "docs/list.md",
    "content": "# list\n\nUtils for handling list.\n\n## list.uniq\n```python\nuniq(seq, key=None)\n```\n\nRemoves duplicate elements from a list while preserving the order of the rest.\n\nThe value of the optional `key` parameter should be a function that\ntakes a single argument and returns a key to test the uniqueness.\n\n```python\n>>> from pydu.list import uniq\n>>> uniq([1, 4, 0, 2, 0, 3])\n[1, 4, 0, 2, 3]\n```\n\n\n## list.tolist\n```python\ntolist(obj)\n```\n\nConvert given `obj` to list.\n\nIf `obj` is not a list, return `[obj]`, else return `obj` itself.\n\n```python\n>>> from pydu.list import tolist\n>>> tolist('foo')\n['foo']\n```\n\n\n## list.flatten\n```python\nflatten(seq)\n```\n\nGenerate each element of the given `seq`. If the element is iterable and\nis not string, it yields each sub-element of the element recursively.\n\n```python\n>>> from pydu.list import flatten\n>>> flatten([1, [2, [3, 4]]])\n[1, 2, 3, 4]\n```\n"
  },
  {
    "path": "docs/misc.md",
    "content": "# misc\n\nMiscellaneous utils like `timeout`, `trace` and so on.\n\n## misc.timeout\n```python\ntimeout(seconds)\n```\n\nThis func decorates any func which may be hang for a while. The param `seconds`\ncan be either integer or float.\nIn `test.py`, you may write like below:\n\n```python\nimport time\nfrom pydu.misc import unix_timeout\n@timeout(1)\ndef f():\ntime.sleep(1.01)\nf()\n```\n\nAnd run `test.py`, will see `TimeoutError`.\n\n\n## misc.trace\n```python\ntrace(obj)\n```\n\nTracing every statement and line number for running program, like `bash -x`.\nIn `test.py`, you may write like below:\n\n```python\nfrom pydu.misc import trace\n@trace\ndef f():\nprint(1)\na = 1 + 5\nb = [a]\nprint(2)\nf()\n```\n\nAnd run `test.py`, will see below output from console:\n\n```console\ntest.py(4):     print(1)\n1\ntest.py(5):     a = 1 + 5\ntest.py(6):     b = [a]\ntest.py(7):     print(2)\n2\n```\n\n\n## misc.memoize\n```python\nmemoize(obj)\n```\n\nA simple memoize decorator for functions supporting (hashable)\npositional arguments.\nIt also provides a `cache_clear()` function for clearing the cache.\n\n```python\n>>> @memoize\n... def foo()\n...     return 1\n...\n>>> foo()\n1\n>>> foo.cache_clear()\n>>>\n```\n\n\n## misc.memoize_when_activated\n```python\nmemoize_when_activated(obj)\n```\n\nA memoize decorator which is disabled by default. It can be\nactivated and deactivated on request.\nFor efficiency reasons it can be used only against class methods\naccepting no arguments.\n\n```python\n>>> class Foo:\n...     @memoize\n...     def foo()\n...         print(1)\n...\n>>> f = Foo()\n>>> # deactivated (default)\n>>> foo()\n1\n>>> foo()\n1\n>>>\n>>> # activated\n>>> foo.cache_activate()\n>>> foo()\n1\n>>> foo()\n>>> foo()\n>>>\n```\n\n\n## misc.super_len\n```python\nsuper_len(obj)\n```\n\nGet length of object which has attribute named `__len__`, `len`, `fileno`, `tell`,\nsuch as `list`, `tuple`, `dict`, `file` and so on.\n\n```python\n>>> from pydu.misc import super_len\n>>> super_len([1, 2])\n2\n>>> super_len(open('test', 'w'))\n0\n```\n"
  },
  {
    "path": "docs/network.md",
    "content": "# network\n\nUtils for handling network.\n\n## network.dotted_netmask\n```python\ndotted_netmask(mask)\n```\n\nConverts mask from /`xx` format to `xxx.xxx.xxx.xxx`.\n`mask` can be either `int` or `str`.\n\n```python\n>>> from pydu.network import dotted_netmask\n>>> dotted_netmask('24')\n'255.255.255.0'\n>>> dotted_netmask(24)\n'255.255.255.0'\n```\n\n\n## network.private_ipv4s\n\n```python\nprivate_ipv4s\n```\n\nA list of private ipv4 addresses. Each item is a tuple of\n(ipv4 address, mask).\n\n## network.is_ipv4\n```python\nis_ipv4(ip)\n```\n\nJudge whether the given `ip` is IPV4 address.\n\n```python\n>>> from pydu.network import is_ipv4\n>>> is_ipv4('8.8.8.8')\nTrue\n>>> is_ipv4('localhost.localdomain')\nFalse\n```\n\n\n## network.is_ipv6\n```python\nis_ipv6(ip)\n```\n\nJudge whether the given `ip` is IPV6 address.\n\n```python\n>>> from pydu.network import is_ipv6\n>>> is_ipv6('fe80::9e5b:b149:e187:1a18')\nTrue\n>>> is_ipv6('localhost.localdomain')\nFalse\n```\n\n\n## network.get_free_port\n```python\nget_free_port()\n```\n\nGet free port which could be bound.\n\n```python\n>>> from pydu.network import get_free_port\n>>> get_free_port()\n57118\n```\n\n\n## network.ip2int\n```python\nip2int(ip_str)\n```\n\nConvert ip to integer. Support IPV4 and IPV6.\nRaise `ValueError` if convert failed.\n\n```python\n>>> from pydu.network import ip2int\n>>> ip2int('10.1.1.1')\n167837953\n```\n\n\n## network.int2ip\n```python\nint2ip(ip_int)\n```\n\nConvert integer to ip. Support IPV4 and IPV6.\nRaise `ValueError` if convert failed.\n\n```python\n>>> from pydu.network import int2ip\n>>> int2ip(167837953)\n'10.1.1.1'\n```\n"
  },
  {
    "path": "docs/path.md",
    "content": "# path\n\nUtils for handling path.\n\n## path.cd\n```python\ncd(path)\n```\n\nContext manager for cd the given path.\n\n```python\n>>> from pydu.path import cd\n>>> with cd('test'):\n...     pass\n```\n\n\n## path.is_super_path\n```python\nis_super_path(path1, path2)\n```\n\nWhether `path1` is the super path of `path2`.\nNote that if `path1` is same as `path2`, it's also regarded as\nthe super path os `path2`.\n\nFor instance \"/\", \"/opt\" and \"/opt/test\" are all the super paths of \"/opt/test\",\nwhile \"/opt/t\" is the super path of \"/opt/test\".\n\n```python\n>>> from pydu.path import is_super_path\n>>> is_super_path('/aa/bb/cc', '/aa/bb/cc')\nTrue\n>>> is_super_path('/aa/bb', '/aa/bb/cc')\nTrue\n>>> is_super_path('/aa/b', '/aa/bb/cc')\nFalse\n```\n\n\n## path.normjoin\n```python\nnormjoin(path)\n```\n\nJoin one or more path components intelligently and normalize it.\n\n```python\n>>> from pydu.path import normjoin\n>>> normjoin('/a', '../b')\n'/b'\n```\n\n\n## path.filename\n```python\nfilename(path)\n```\n\nReturn the filename without extension.\n\n```python\n>>> from pydu.path import filename\n>>> filename('/foo/bar.ext')\n'bar'\n```\n\n\n## path.fileext\n```python\nfileext(path)\n```\n\nReturn the file extension.\nIf file has not extension, return empty string.\n\n```python\n>>> from pydu.path import fileext\n>>> filename('/foo/bar.ext')\n'.ext'\n```\n"
  },
  {
    "path": "docs/platform.md",
    "content": "# platform\n\nConstants which indicates specific platform.\n\n## platform.WINDOWS\n\nJudge whether current platform is WINDOWS or not.\n\n\n## platform.LINUX\n\nJudge whether current platform is LINUX or not.\n\n\n## platform.POSIX\n\nJudge whether current platform is POSIX or not.\n\n\n## platform.DARWIN\n\nJudge whether current platform is DARWIN or not.\n\n\n## platform.SUNOS\n\nJudge whether current platform is SUNOS or not.\n\n\n## platform.SMARTOS\n\nJudge whether current platform is SMARTOS or not.\n\n\n## platform.FREEBSD\n\nJudge whether current platform is FREEBSD or not.\n\n\n## platform.NETBSD\n\nJudge whether current platform is NETBSD or not.\n\n\n## platform.OPENBSD\n\nJudge whether current platform is OPENBSD or not.\n\n\n## platform.AIX\n\nJudge whether current platform is AIX or not.\n"
  },
  {
    "path": "docs/process.md",
    "content": "# process\n\nUtils for handling processes.\n\n`process` is based on `psutil`. Need to `pip install psutil` first.\n\n\n## process.get_processes_by_path\n```python\nget_processes_by_path(path)\n```\n\nGet processes which are running on given path or sub path of given path.\n\n```python\n>>> from pydu.process import get_processes_by_path\n>>> get_processes_by_path('/usr/bin/python')\n[{'cmdline': '/usr/bin/python2.7', 'pid': 23383, 'name': 'python'}]\n```\n"
  },
  {
    "path": "docs/request.md",
    "content": "# Request\n\nUtils for handling request.\n\n## request.Filename\n\nSupply several methods to get filename.\n\n```python\nFilename.from_url(url)\n```\n\nDetected filename as unicode or None.\n\n```python\nFilename.from_headers(headers)\n```\n\nDetect filename from Content-Disposition headers if present.\n`headers` could be a dict, list or string.\n\n```python\nFilename.from_any(dst=None, headers=None, url=None)\n```\n\nDetect filename from dst or headers or url.\n\n\n## request.download\n```python\nFilename.download(url, dst=None)\n```\n\nHigh level function, which downloads URL into tmp file in current\ndirectory and then renames it to filename autodetected from either URL\nor HTTP headers.\n`url` indicates which url to download.\n`dst` is the filename or directory of destination. `None` as default, means\ndownload to current directory.\n\n\n## request.check_connect\n```python\ncheck_connect(ip, port, retry=1, timeout=0.5)\n```\n\nCheck whether given `ip` and `port` could connect or not.\nIt will `retry` and `timeout` on given.\n\n```python\n>>> from pydu.request import check_connect\n>>> check_connect('http://www.baidu.com', 80)\n'192.168.3.8'\n```\n\n\n## request.update_query_params\n```python\nupdate_query_params(url, params)\n```\n\nUpdate query params of given url and return new url.\n\n```python\n>>> from pydu.request import update_query_params\n>>> update_query_params('http://example.com', {'foo': 1})\n'http://example.com?foo=1'\n```\n\n\n## request.cookies_str_to_dict\n```python\ncookies_str_to_dict(cookies)\n```\n\nConvert cookies from str to dict.\n\n```python\n>>> from pydu.request import cookies_str_to_dict\n>>> cookies_str_to_dict('a=a;b=b')\n{'a': 'a', 'b': 'b'}\n```\n"
  },
  {
    "path": "docs/set.md",
    "content": "# Set\n\nAdditional powerful sets.\n\n## set.OrderedSet\n```python\nOrderedSet(iterable=None)\n```\n\nA set which keeps the ordering of the inserted items.\n\n```python\n>>> from pydu.set import OrderedSet\n>>> s = OrderedSet([1, 3, 1, 2])\n>>> list(s)\n[1, 3, 2]\n>>> s.discard(3)\n>>> list(s)\n[1, 2]\n```\n"
  },
  {
    "path": "docs/slot.md",
    "content": "# slot\n\n## slot.SlotBase\n```python\nSlotBase(*args, **kwargs)\n```\n\nBase class for class using `__slots__`.\nIf some args or kwargs are not given when initialize class,\nthe value of them will be set with `None`.\n\n```python\n>>> from pydu.slot import SlotBase\n>>> class Foo(SlotBase):\n        __slots__ = ('a', 'b', 'c')\n>>> foo = Foo(1, b=2)\n>>> foo.a\n1\n>>> foo.b\n2\n>>> foo.c\n>>>\n```\n"
  },
  {
    "path": "docs/string.md",
    "content": "# String\n\nUtils for handling string.\n\n## string.safeunicode\n```python\nsafeunicode(obj, encoding='utf-8')\n```\n\nConverts any given object to unicode string.\n\n```python\n>>> from pydu.string import safeunicode\n>>> safeunicode('hello')\nu'hello'\n>>> safeunicode(2)\nu'2'\n>>> safeunicode('\\xe4\\xb8\\xad\\xe6\\x96\\x87')\nu'中文'\n```\n\n\n## string.safeencode\n```python\nsafeencode(obj, encoding='utf-8')\n```\n\nConverts any given object to encoded string (default: utf-8).\n\n```python\n>>> from pydu.string import safeencode\n>>> safeencode('hello')\n'hello'\n>>> safeencode(2)\n'2'\n>>> safeencode(u'中文')\n'\\xe4\\xb8\\xad\\xe6\\x96\\x87'\n```\n\n\n## string.lstrips\n```python\nlstrips(text, remove)\n```\n\nRemoves the string `remove` from the left of `text`.\n\n```python\n>>> from pydu.string import lstrips\n>>> lstrips('foobar', 'foo')\n'bar'\n>>> lstrips('FOOBARBAZ', ['FOO', 'BAR'])\n'BAZ'\n>>> lstrips('FOOBARBAZ', ['BAR', 'FOO'])\n'BARBAZ'\n```\n\n\n## string.rstrips\n```python\nrstrips(text, remove)\n```\n\nRemoves the string `remove` from the right of `text`.\n\n```python\n>>> from pydu.string import rstrips\n>>> rstrips('foobar', 'bar')\n'foo'\n```\n\n\n## string.strips\n```python\nstrips(text, remove)\n```\n\nRemoves the string `remove` from the both sides of `text`.\n\n```python\n>>> from pydu.string import strips\n>>> strips('foobarfoo', 'foo')\n'bar'\n```\n\n## string.common_prefix\n```python\ncommon_prefix(l)\n```\n\nReturn common prefix of the stings\n\n```python\n>>> from pydu.string import common_prefix\n>>> common_prefix(['abcd', 'abc1'])\n'abc'\n```\n\n\n## string.common_suffix\n```python\ncommon_suffix(l)\n```\n\nReturn common suffix of the stings\n\n```python\n>>> from pydu.string import common_suffix\n>>> common_suffix(['dabc', '1abc'])\n'abc'\n```\n\n\n## string.sort\n```python\nsort(s, reversed=False)\n```\n\nSort given string by ascending order.\nIf `reverse` is `True`, sorting given string by descending order.\n\n```python\n>>> from pydu.string import sort\n>>> sort('dabc')\n'abcd'\n```\n"
  },
  {
    "path": "docs/system.md",
    "content": "# System\n\nUtils for handling system, like to track file, make directory, link and so on.\n\n\n## system.FileTracker\n```python\nFileTracker()\n```\n\nTrack current opening files, started with `FileTracker.track()`.\nWhen opening several files, `FileTracker` tracks them and you can locate them by calling\n`FileTraker.get_openfiles()`.\n\n```python\nFiltTracker.track()\n```\n\nStart tracking opening files.\n\n```python\nFiltTracker.untrack()\n```\n\nStop tracking opening files.\n\n```python\nFiltTracker.get_openfiles()\n```\n\nGet current opening files.\n\n```python\n>>> from pydu.system import FileTracker\n>>> FileTracker.track()\n>>> f = open('test', 'w')\n>>> FileTracker.get_openfiles()\n{<_io.TextIOWrapper name='test' mode='w' encoding='UTF-8'>}\n>>> f.close()\n>>> FileTracker.get_openfiles()\nset()\n>>> FileTracker.untrack()\n>>> f = open('test', 'w')\n>>> FileTracker.get_openfiles()\nset()\n```\n\n\n## system.makedirs\n```python\nmakedirs(path, mode=0o755, ignore_errors=False, exist_ok=False)\n```\n\nBased on `os.makedirs`,create a leaf directory and all intermediate ones.\n`mode` default is `0o755`. When make an exists path, if exist_ok is false,\n`makedirs` will raise an `Exception`. If `ignore_errors` which will ignore\nall errors raised by `os.makedirs`.\n\n```python\n>>> from pydu.system import makedirs\n>>> makedirs('test1/test2')\n>>> makedirs('test1',exist_ok=True)\n>>> makedirs('test1')\nTraceback (most recent call last):\n...    OSError: Create dir: test1 error.\n```\n\n## system.remove\n```python\nremove(path, mode=0o755, ignore_errors=False, onerror)\n```\n\nRemove a file or directory.\n\nIf `ignore_errors` is set, errors are ignored; otherwise, if `onerror`\nis set, it is called to handle the error with arguments (`func` ,\n`path` , `exc_info` ) where func is platform and implementation dependent;\n`path` is the argument to that function that caused it to fail; and\n`exc_info` is a tuple returned by `sys.exc_info()`.  If `ignore_errors`\nis `False` and `onerror` is None, it attempts to set `path` as writeable and\nthen proceed with deletion if `path` is read-only, or raise an exception\nif `path` is not read-only.\n\n```python\n>>> from pydu.system import makedirs\n>>> from pydu.system import remove\n>>> from pydu.system import touch\n>>> makedirs('test')\n>>> remove('test')\n>>> touch('test.txt')\n>>> remove('test.txt')\n>>> remove('test.txt', ignore_errors=True)\n>>> remove('test.txt')\nTraceback (most recent call last):\n...    OSError: Remove path: test error. Reason: [Errno 2] No such file or directory: 'test.txt'\n```\n\n## system.removes\n```python\nremoves(paths, mode=0o755, ignore_errors=False, onerror)\n```\n\nRemove a list of file and/or directory.Other parameters same as `remove`.\n\n```python\n>>> from pydu.system import makedirs\n>>> from pydu.system import remove\n>>> from pydu.system import open_file\n>>> makedirs('test1')\n>>> makedirs('test2')\n>>> open_file('test.txt')\n>>> removes(['test.txt','test1','test2'])\n```\n\n## system.open_file\n```python\nopen_file(path, mode='wb+', buffer_size=-1, ignore_errors=False):\n```\n\nOpen a file, defualt mode `wb+`. If path not exists, it will be created\nautomatically. If `ignore_errors` is set, errors are ignored.\n\n```python\n>>> from pydu.system import open_file\n>>> open_file('test.txt')\n>>> ls\ntest.txt\n>>> open_file('test1.txt',mode='r')\nTraceback (most recent call last):\n...    OSError: Open file: test1.txt error\n```\n\n## system.copy\n```python\ncopy(src, dst, ignore_errors=False, follow_symlinks=True):\n```\n\nCopy data and mode bits (`cp src dst`).Both the source and destination\nmay be a directory.When `copy` a directory,which contains a symlink,if\nthe optional symlinks flag is true, symbolic  links in the source tree\nresult in symbolic links in the  destination tree; if it is false, the\ncontents of the files pointed to by symbolic links are copied.When copy\na file,if follow_symlinks is false and src is a symbolic link, a new\nsymlink will be created instead of copying the file it points to,else\nthe contents of the file pointed to by symbolic links is copied.\n\n```python\n>>> from pydu.system import copy,symlink\n>>> from pydu.system import makedirs,open_fle\n>>> open_fle('test/test.txt')\n>>> symlink('test/test.txt','test/test.link')\n>>> copy('test/test.link','test/test_copy1.link')\n>>> copy('test/test.link','test/test_copy2.link',follow_symlink=False)\n```\n\n## system.touch\n```python\ntouch(path):\n```\n\nMake a new file.\n\n```python\n>>> from pydu.system import touch\n>>> touch('test.txt')\n```\n\n## system.symlink\n```python\nsymlink(src, dst, overwrite=False, ignore_errors=False)\n```\n\nIt create a symbolic link pointing\nto source named link_name.If dist is exist and overwrite is true,a new\nsymlink will be created.\n\n```python\n>>> from pydu.system import symlink\n>>> symlink('test.txt','test.link')\n```\n\n!> `symlink` can only be used on `unix-like` system.\n\n## system.link\n```python\nlink(src, dst, overwrite=False, ignore_errors=False):\n```\n\nIt create a hard link pointing to\nsource named link_name.If dist is exist and overwrite is true, a\nnew link will be created.\n\n```python\n>>> from pydu.system import link\n>>> link('test.txt','test.link')\n```\n\n!> `link` can only be used on `unix-like` system.\n\n\n## system.which\n```python\nwhich(cmd, mode=os.F_OK | os.X_OK, path=None):\n```\n\nGiven a command, mode, and a PATH string, return the path which\nconforms to the given mode on the PATH, or None if there is no such\nfile.\n\n`mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result\nof os.environ.get(\"PATH\"), or can be overridden with a custom search\npath.\n\n`which` is `shutil.which` in Python 3.\n\n```python\n>>> from pydu.system import which\n>>> which('echo')\n/bin/echo\n```\n\n\n## system.chmod\n```python\nchmod(path, mode, recursive=False)\n```\n\nChange permissions to the given mode.\nIf `recursive` is True perform recursively.\n\n```python\n>>> from pydu.system import chmod\n>>> chmod('/opt/sometest', 0o744)\n>>> oct(os.stat('/opt/sometest').st_mode)[-3:]\n'744'\n```\n\n!> Although Windows supports `chmod`, you can only set the file’s\nread-only flag with it (via the stat.S_IWRITE and stat.S_IREAD constants\nor a corresponding integer value). All other bits are ignored.\n\n\n## system.chcp\n```python\nchcp(code)\n```\n\nContext manager which sets the active code page number.\nIt could also be used as function.\n\n```python\n>>> from pydu.system import chcp\n>>> chcp(437)\n<active code page number: 437>\n>>> with chcp(437):\n...     pass\n>>>\n```\n\n!> `chcp` can only be used on `Windows` system.\n\n\n## system.preferredencoding\n```python\npreferredencoding(code)\n```\n\nGet best encoding for the system.\n"
  },
  {
    "path": "docs/unit.md",
    "content": "# Unit\n\nUtils for handling unit.\n\n## unit.Bytes\n```python\nBytes(bytes)\n```\n\nSupply several methods dealing with bytes.\n\n```python\nBytes.convert(self, unit=None, multiple=1024)\n```\n\nConvert bytes with given `unit`.\nIf `unit` is `None`, convert bytes with suitable unit.\nConvert `multiple` is default to be 1024.\n\n```python\n>>> Bytes(1024).convert()\n(1, 'KB')\n```\n"
  },
  {
    "path": "docs/zh-cn/README.md",
    "content": "## pydu\n\n> **pydu(Python Data structures and Utils)** 是面向Python 2 和 3 的实用数据结构和工具库。\n它收集自开源项目，也有来自开发者贡献。\n\n\n## 安装\n要安装**pydu**，简单执行：\n\n```bash\n$ pip install pydu\n```\n"
  },
  {
    "path": "docs/zh-cn/_sidebar.md",
    "content": "* 模块\n\n  * [Archive 归档](zh-cn/archive.md)\n  * [Commad 命令](zh-cn/cmd.md)\n  * [Compat 兼容性](zh-cn/compat.md)\n  * [Console 控制台](zh-cn/console.md)\n  * [Convert 转换](zh-cn/convert.md)\n  * [Dict 字典](zh-cn/dict.md)\n  * [Date and Time 时间](zh-cn/dt.md)\n  * [Environment 环境](zh-cn/environ.md)\n  * [Exception 异常](zh-cn/exception.md)\n  * [Functional 函数式](zh-cn/functional.md)\n  * [Inspect 检查](zh-cn/inspect.md)\n  * [Iter 迭代](zh-cn/iter.md)\n  * [List 列表](zh-cn/list.md)\n  * [Miscellanea 综合](zh-cn/misc.md)\n  * [Network 网络](zh-cn/network.md)\n  * [Path 路径](zh-cn/path.md)\n  * [Platform 平台](zh-cn/platform.md)\n  * [Process 进程](zh-cn/process.md)\n  * [Request 请求](zh-cn/request.md)\n  * [Set 集合](zh-cn/set.md)\n  * [Slot 槽](zh-cn/slot.md)\n  * [String 字符串](zh-cn/string.md)\n  * [System 系统](zh-cn/system.md)\n  * [Unit 单位](zh-cn/unit.md)\n\n* [变更记录](changelog.md)\n"
  },
  {
    "path": "docs/zh-cn/archive.md",
    "content": "# archive\n\n提供归档相关工具，如解压。\n\n## archive.extract\n```python\nextract(path, to_path='', ext='')\n```\n\n解压tar或zip文件，可指定 ``to_path`` 解压到特定目录。它支持很多文件格式，包括 \"\n\"``.tar``、``.tar.bz2``、``.tar.gz``、``.tgz``、``.tz2``、``.zip``。如果给定的 \"\n\"``path`` 不包含文件格式，则可指定 ``ext`` 参数来说明文件格式。\n\n```python\n>>> from pydu.archive import extract\n>>> extract('foobar.tgz', '/tmp')\n>>> extract('foobar', '/tmp', ext='.tgz')\n>>> extract('foobar', '/tmp')\nTraceback (most recent call last):\n  ...    AttributeError: pydu.archive.UnrecognizedArchiveFormat: Path not a recognized archive format: foobar\n```\n"
  },
  {
    "path": "docs/zh-cn/cmd.md",
    "content": "# cmd\n\n提供运行命令和获取命令行等工具。\n\n## cmd.TimeoutExpired\n```python\nTimeoutExpired(cmd, timeout, output=None, stderr=None)\n```\n\n该异常在等待子进程超时时抛出。\n\n属性：\n    cmd, output, stdout, stderr, timeout\n\n\n## cmd.run\n```python\nrun(cmd, shell=False, env=None, timeout=None, timeinterval=1)\n```\n\nRun 命令基于 `subprocess.Popen` ，并返回 `(returncode, stdout)` 的这样元组。\n\n注意，`stderr` 被重定向到了 `stdout`。`shell` 同 `Popen` 中的参数一样。\n\n如果在 `timeout` 秒后进程没有退出，将会抛出 `TimeoutExpired` 异常。`timeinterval` \n在Python 2中给定 timeout时生效。它表示进程状态检查时间间隔。\n\n如果超时了，子进程不会被杀掉。为了合理清除表现良好的应用，应该要杀掉子进程，并且结束通信。\n\n```python\n>>> from pydu.cmd import run\n>>> run('echo hello')\n(0, b'hello\\r\\n')  # Python 3\n```\n\n\n## cmd.run_with_en_env\n```python\nrun_with_en_env(cmd, shell=False, env=None, timeout=None, timeinterval=1)\n```\n\n在英文字符集环境下运行命令，从而得到英文输出。参数同 `run`。\n\n\n## cmd.terminate\n```python\nterminate(pid)\n```\n\n根据给定 `pid` 终止进程。\n\n在Windows上，使用 `kernel32.TerminateProcess` 来终止。在其他平台上，使用携带 `signal.SIGTERM` 信号的 `os.kill` 来终止。\n\n\n## cmd.cmdline_argv\n```python\ncmdline_argv()\n```\n\n获取当前Python进程的命令行参数。在Windows上使用Python 2时， `cmdline_argv` 的\n实现是基于 `shell32.GetCommandLineArgvW` 获取列表元素为Unicode字符串形式的`sys.argv`。\n\n而在其他平台或者是使用Python 3时， `cmdline_argv` 和 `sys.argv` 相同。\n\n```python\n>>> from pydu.cmd import cmdline_argv\n>>> cmdline_argv()\n['/Applications/PyCharm.app/Contents/helpers/pydev/pydevconsole.py', '61253', '61254']\n```\n"
  },
  {
    "path": "docs/zh-cn/compat.md",
    "content": "# compat\n\n提供Python 2和3兼容的数据结构、库和函数。\n\n## compat.PY2\n\n判断当前Python解释器是Python 2还是3。\n\n\n## compat.urlib\n```python\nurlib(base, url, allow_fragments=True)\n```\n\n在PY2中是 ``urllib``，在PY3中是 ``urllib.request``。\n\n\n## compat.urlparse\n```python\nurlparse(base, url, allow_fragments=True)\n```\n\n在PY2中是 ``urlparse``，在PY3中是 ``urllib.parse``。\n\n\n## compat.urljoin\n```python\nurljoin(base, url, allow_fragments=True)\n```\n\n在PY2中是 ``urlparse.urljoin``，在PY3中是 ``urllib.parse.urljoin``。\n\n\n## compat.iterkeys\n```python\niterkeys(d)\n```\n\n返回字典键的iter对象。\n\n\n## compat.itervalues\n```python\nitervalues(d)\n```\n\n返回字典值的iter对象。\n\n\n## compat.iteritems\n```python\niteritems(d)\n```\n\n返回字典键值对的iter对象。\n\n\n## compat.text_type\n\ntext类型在PY2中是 ``unicode``，在PY3中是 ``str``。\n\n\n## compat.string_types\n\nstring类型在PY2中是 ``(str, unicode)``，在PY3中是 ``(str,)``。\n\n## compat.strbytes_types\n\nstrbytes（string bytes）类型在PY2中是 ``(str, unicode, bytes)``，在PY3中是 ``(str, \"\n\"bytes)``。\n\n\n## compat.numeric_types\n\n在PY2中是 ``(int, long)``，在PY3中是 ``(int,)``。\n\n\n## compat.imap\n```python\nimap(func, *iterables)\n```\n\n在PY2中是 ``itertools.imap``，在PY3中是 ``map``。\n\n\n## compat.izip\n```python\nizip(iter1 [,iter2 [...])\n```\n\n在PY2中是 ``itertools.izip``，在PY3中是 ``zip``。\n\n\n## compat.reduce\n```python\nreduce(function, sequence, initial=None)\n```\n\n在PY2中是内建 ``reduce``，在PY3中是 ``functools.reduce``。\n\n\n## compat.cmp\n```python\ncmp(x, y)\n```\n\nSame to ``cmp`` on PY2, but implement on PY3.\n在PY2中是内建 ``cmp``，在PY3中则由pydu实现。\n\n\n## compat.has_next_attr\n```python\nhas_next_attr(x)\n```\n\n查看是否有next属性。\n\n\n## compat.is_iterable\n```python\nis_iterable(x)\n```\n\n查看是否是可迭代的。\n\n```python\n>>> from pydu.compat import is_iterable\n>>> is_iterable([])\nTrue\n>>> is_iterable(1)\nFalse\n```\n"
  },
  {
    "path": "docs/zh-cn/console.md",
    "content": "# Console\n\n提供处理控制台的工具。\n\n## console.console_size\n```python\nconsole_size(fallback=(80, 25))\n```\n\n对于Windows系统，返回可用窗口区域的(width, height)。如果没有控制台，则返回fallback。\n对于POSIX系统，返回控制终端的(width, height)。如果遇到IOError，比如没有控制台，返回fallback。\n对于其他系统，返回fallback。Fallback默认为(80, 25)，这是大多数终端模拟器的默认大小。\n\n```python\n>>> from pydu.console import console_size\n>>> console_size()\n(80, 25)\n```\n"
  },
  {
    "path": "docs/zh-cn/convert.md",
    "content": "# Convert\n\n提供将一类数据转换为另一类的工具。\n\n\n## convert.boolean\n```python\nboolean(obj)\n```\n\n将对象转换为布尔值。\n\n如果对象是字符串，将会以不区分大小写的形式转换：\n\n* 将 `yes`、 `y`、 `on`、 `true`、 `t`、 `1` 转换为True\n* 将 `no`、 `n`、 `off`、 `false`、 `f`、 `0` 转换为False\n* 如果传入其他值，抛出TypeError\n\n如果对象不是字符串，将会使用 `bool(obj)` 转换。\n\n```python\n>>> from pydu.string import boolean\n>>> boolean('yes')\nTrue\n>>> boolean('no')\nFalse\n```\n\n\n## convert.bin2oct\n```python\nbin2oct(x)\n```\n\n把二进制字符串转换为八进制字符串。\n比如：'1001' -> '11'\n\n```python\n>>> from pydu.convert import bin2oct\n>>> bin2oct('1001')\n'11'\n```\n\n\n## convert.bin2dec\n```python\nbin2dec(x)\n```\n\n把二进制字符串转换为十进制数字。\n比如：'11' -> 3\n\n```python\n>>> from pydu.convert import bin2dec\n>>> bin2dec('11')\n3\n```\n\n\n## convert.bin2hex\n```python\nbin2hex(x)\n```\n\n把二进制字符串转换为十六进制字符串。\n比如：'11010' -> '1a'\n\n```python\n>>> from pydu.convert import bin2hex\n>>> bin2hex('11010')\n'1a'\n```\n\n\n## convert.oct2bin\n```python\noct2bin(x)\n```\n\n把八进制字符串转换为二进制字符串。\n比如：'11' -> '1001'\n\n```python\n>>> from pydu.convert import oct2bin\n>>> oct2bin('11')\n'1001'\n```\n\n\n## convert.oct2dec\n```python\noct2dec(x)\n```\n\n把八进制字符串转换为十进制数字。\n比如：'11' -> 9\n\n```python\n>>> from pydu.convert import oct2dec\n>>> oct2dec('11')\n9\n```\n\n\n## convert.oct2hex\n```python\noct2hex(x)\n```\n\n把八进制字符串转换为十六进制字符串。\n比如：'32' -> '1a'\n\n```python\n>>> from pydu.convert import oct2hex\n>>> oct2hex('32')\n'1a'\n```\n\n\n## convert.dec2bin\n```python\ndec2bin(x)\n```\n\n把十进制数字转换为二进制字符串。\n比如：3 -> '11'\n\n```python\n>>> from pydu.convert import dec2bin\n>>> dec2bin(3)\n'11'\n```\n\n\n## convert.dec2oct\n```python\ndec2oct(x)\n```\n\n把十进制数字转换为八进制字符串。\n比如：9 -> '11'\n\n```python\n>>> from pydu.convert import dec2oct\n>>> dec2oct(9)\n'11'\n```\n\n\n## convert.dec2hex\n```python\ndec2hex(x)\n```\n\n把十进制数字转换为十六进制字符串。\n比如：26 -> '1a'\n\n```python\n>>> from pydu.convert import dec2hex\n>>> dec2hex(26)\n'1a'\n```\n\n\n## convert.hex2bin\n```python\nhex2bin(x)\n```\n\n把十六进制字符串转换为二进制字符串。\n比如：'1a' -> '11010'\n\n```python\n>>> from pydu.convert import hex2bin\n>>> hex2bin('1a')\n'11010'\n```\n\n\n## convert.hex2oct\n```python\nhex2oct(x)\n```\n\n把十六进制字符串转换为八进制字符串。\n比如：'1a' -> '32'\n\n```python\n>>> from pydu.convert import hex2oct\n>>> hex2oct('1a')\n'32'\n```\n\n\n## convert.hex2dec\n```python\nhex2dec(x)\n```\n\n把十六进制字符串转换为十进制数字。\n比如：'1a' -> 26\n\n```python\n>>> from pydu.convert import hex2dec\n>>> hex2dec('1a')\n26\n```\n"
  },
  {
    "path": "docs/zh-cn/dict.md",
    "content": "# Dict\n\n增强的字典和相关函数。\n\n## dict.AttrDict\n```python\nAttrDict(seq=None, **kwargs)\n```\n\nAttrDict 对象类似于字典，除了能使用 `obj['foo']`，还能使用 `obj.foo`。\n\n```python\n>>> from pydu.dict import AttrDict\n>>> o = AttrDict(a=1)\no.a\n1\n>>> o['a']\n1\n>>> o.a = 2\n>>> o['a']\n2\n>>> del o.a\n>>> o.a\nTraceback (most recent call last):\n...    AttributeError: 'a'\n```\n\n\n## dict.CaseInsensitiveDict\n```python\nCaseInsensitiveDict(data=None, **kwargs)\n```\n\n大小写不敏感类 `字典` 对象。实现了 `collections.MutableMapping` 的所有方法和操作，\n也实现了字典的 `copy`，此外还提供 `lower_items`。所有的键都应是字符串。\n内部结构会记住最后一次被设置的键的大小写，`iter(instance)`、`keys()`、`items()`、\n`iterkeys()` 和 `iteritems()` 将会包含大小写敏感的键。\n\n```python\n>>> from pydu.dict import CaseInsensitiveDict\n>>> cid = CaseInsensitiveDict()\n>>> cid['Accept'] = 'application/json'\n>>> cid['aCCEPT'] == 'application/json'\nTrue\n>>> list(cid) == ['Accept']\nTrue\n```\n\n\n## dict.LookupDict\n```python\nLookupDict(name=None)\n```\n\n字典查找对象。\n\n```python\n>>> from pydu.dict import LookupDict\n>>> d = LookupDict()\n>>> d['key']\nNone\n>>> d['key'] = 1\n>>> d['key']\n1\n```\n\n## dict.OrderedDefaultDict\n```python\nOrderedDefaultDict(default_factory=None, *args, **kwds)\n```\n\n记住插入顺序且能根据默认工厂提供默认值的字典。\n\n当key不存在（仅限通过 `__getitem__` 中）时，无参数调用默认工厂来产生新值。\n`OrderedDefaultDict` 和 `collections.defaultdict` 在比较时是等同的。\n所有剩余参数和传入 `defaultdict` 构造器中的相同，包括关键字参数。\n\n```python\n>>> from pydu.dict import OrderedDefaultDict\n>>> d = OrderedDefaultDict(int)\n>>> d['b']\n0\n>>> d['a']\n0\n>>> d.keys()\nodict_keys(['b', 'a'])\n```\n\n\n## dict.attrify\n```python\nattrify(obj)\n```\n\n将对象属性化为 `AttriDict` 或 包含 `AttriDict` 的列表（如果对象为列表）。\n如果对象或对象中的元素不是列表或字典，将会返回其本身。\n\n```python\n>>> from pydu.dict import attrify\n>>> attrd = attrify({\n'a': [1, 2, {'b': 'b'}],\n'c': 'c',\n})\n>>> attrd\n<AttrDict {'a': [1, 2, <AttrDict {'b': 'b'}>], 'c': 'c'}>\n>>> attrd.a\n1\n>>> attrd.a[2].b\nb\n>>> attrd.c\nc\n```\n"
  },
  {
    "path": "docs/zh-cn/dt.md",
    "content": "# Date and Time\n\n提供处理日期和时间的工具。\n\n\n## dt.timer\n```python\ntimer(path)\n```\n\ntimer可以上下文管理器或装饰器的方式统计一次调用的时间。\n如果将 `print_func` 赋值为 `sys.stdout.write`、 `logger.info` 或其他，\ntimer将会打印所花时长。\n\n```python\ntimeit = timer(print_func=sys.stdout.write)\nwith timeit:\nfoo()\n\n@timeit\ndef foo():\npass\n```\n\n`timer.elapsed` 包含了 `foo` 所花费的整个时间。\n\n```python\n>>> timeit = timer(print_func=sys.stdout.write)\n>>> with timeit:\n...     os.getcwd()\nSpent time: 1.7881393432617188e-05s\n```\n"
  },
  {
    "path": "docs/zh-cn/environ.md",
    "content": "# Environ\n\n提供处理环境相关内容的工具。\n\n\n## environ.environ\n```python\nenviron(**kwargs)\n```\n\n更新一个或多个环境变量的上下文管理器。\n\n保存先前的环境变量（如果有），并在退出上下文管理器时还原。\n\n如果给定 variable_name=None，表示从环境变量中临时移除该变量。\n\n```python\n>>> from pydu.environ import environ\n>>> with environ(a='a'):\n...     print(os.environ['a'])\n...\na\n```\n\n\n## environ.path\n```python\npath(append=None, prepend=None, replace=None)\n```\n\n更新PATH环境变量的上下文管理器。可将给定的字符串或字符串列表，\n插入在PATH的开头和末尾，也可替换PATH。\n\n```python\n>>> import os\n>>> from pydu.environ import path\n>>> with path(append='/foo'):\n...     print(os.environ['PATH'])\n...\n/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/foo\n```\n"
  },
  {
    "path": "docs/zh-cn/exception.md",
    "content": "# Exception\n\n提供处理异常的工具。\n\n## exception.ignore\n```python\nignore(*exceptions)\n```\n\n忽略给定异常的上下文管理器。\n\n```python\n>>> from pydu.exception import ignore\n>>> with ignore(ValueError, AttributeError):\n...     int('abc')\n...     int.no_exists_func()\n...\n>>>\n```\n\n## exception.default_if_except\n```python\ndefault_if_except(exception_clses, default=None)\n```\n\n捕捉给定异常（可以是一组异常，以元组表示）并返回默认值的异常装饰器。\n\n```python\n>>> from pydu.exception import default_if_except\n>>> @default_if_except(ValueError, default=0)\n... def foo(value):\n...     return int(value)\n>>> foo('abc')\n0\n```\n"
  },
  {
    "path": "docs/zh-cn/functional.md",
    "content": "# functional\n\n提供函数式编程的工具。\n\n## functional.compose\n```python\ncompose(*funcs)\n```\n\n组成所有函数。前一个函数必须接受一个参数，该参数为后一个函数的输出值。\n最后一个函数可以接受任意位置参数和关键字参数。\n`compose(f1, f2, f3)(*x)` 同 `f1(f2(f3(*x)))`。\n\n```python\n>>> from pydu.functional import compose\n>>> def f1(a):\n...     return a+1\n...\n>>> def f2(a, b=2):\n...     return a+b\n...\n>>> compose(f1, f2)(1, b=3)\n5\n```\n"
  },
  {
    "path": "docs/zh-cn/inspect.md",
    "content": "# inspect\n\n提供函数参数检查的工具。\n\n## inspect.getargspec\n```python\ngetargspec(func)\n```\n\n获得函数参数的名称和默认值。\n\n返回由四个字符串组成的元组：(args, vargs, varkw, defaults)。\n`args` 是参数名称的列表（可能包含嵌套列表）。\n`varargs` 和 `varkw` 是 * 和 ** 参数的名称，或者为 `None`。\n`defaults` 是最后n个参数的默认值组成的元组。\n\n```python\n>>> from pydu.inspect import getargspec\n>>> def f(name, address='home', age=25, *args, **kwargs):\n...     pass\n...\n>>> getargspect(f)\nArgSpec(args=['name', 'address', 'age'], varargs='args', keywords='kwargs', defaults=('home', 25))\n```\n\n\n## inspect.get_func_args\n```python\nget_func_args(func)\n```\n\n返回参数名称的列表。诸如 `*args` 和 `*kwargs` 的参数不被包含。\n\n```python\n>>> from pydu.inspect import get_func_args\n>>> def f(name, address='home', age=25, *args, **kwargs):\n...     pass\n...\n>>> get_func_args(f)\n['name', 'address', 'age']\n```\n\n\n## inspect.get_func_full_args\n```python\nget_func_full_args(func)\n```\n\n返回(参数名称, 默认值)元组的列表。如果参数没有默认值，则在元组中丢弃。\n诸如 `*args` 和 `*kwargs` 的参数也被包含在内。\n\n```python\n>>> from pydu.inspect import get_func_full_args\n>>> def f(name, address='home', age=25, *args, **kwargs):\n...     pass\n...\n>>> get_func_full_args(f)\n[('name',), ('address', 'home'), ('age', 25), ('*args',), ('**kwargs',)]\n```\n\n\n## inspect.func_accepts_kwargs\n```python\nfunc_accepts_kwargs(func)\n```\n\n检查函数是否接受关键字参数。\n\n```python\n>>> from pydu.inspect import func_accepts_kwargs\n>>> def f(**kwargs):\n...     pass\n...\n>>> func_accepts_kwargs(f)\nTrue\n```\n\n\n## inspect.func_accepts_var_args\n```python\nfunc_accepts_var_args(func)\n```\n\n检查函数是否接受位置参数。\n\n```python\n>>> from pydu.inspect import func_accepts_var_args\n>>> def f(*vargs):\n...     pass\n...\n>>> func_accepts_var_args(f)\nTrue\n```\n\n\n## inspect.func_supports_parameter\n```python\nfunc_supports_parameter(func)\n```\n\n检查函数是否接受给定参数。\n\n```python\n>>> from pydu.inspect import func_supports_parameter\n>>> def f(name):\n...     pass\n...\n>>> func_supports_parameter(f, 'name')\nTrue\n>>> func_supports_parameter(f, 'unkown')\nFasle\n```\n\n\n## inspect.func_has_no_args\n```python\nfunc_has_no_args(func)\n```\n\n检查函数是否接受任意参数。\n\n```python\n>>> from pydu.inspect import func_has_no_args\n>>> def f():\n...     pass\n...\n>>> func_has_no_args(f)\nTrue\n```\n"
  },
  {
    "path": "docs/zh-cn/iter.md",
    "content": "# iter\n\n提供处理迭代对象的工具。\n\n## iter.first\n```python\nfirst(iterable)\n```\n\n获取可迭代对象的第一个项。\n\n```python\n>>> from pydu.iter import first\n>>> first([1, 2])\n1\n```\n\n\n## iter.last\n```python\nlast(iterable)\n```\n\n获取可迭代对象的最后一个项。注意，由于逐步迭代到最后一项，这可能会较慢。\n\n```python\n>>> from pydu.iter import last\n>>> last([1, 2])\n2\n```\n\n\n## iter.all\n```python\nall(iterable, predicate)\n```\n\n如果给定可迭代对象的所有元素套用判定函数都是True，则返回True。\n\n```python\n>>> from pydu.iter import all\n>>> all([0, 1, 2], lambda x: x+1)\nTrue\n```\n\n\n## iter.any\n```python\nany(iterable)\n```\n\n如果给定可迭代对象的任一元素套用判定函数是True，则返回True。\n\n```python\n>>> from pydu.iter import any\n>>> any([-1, -1, 0], lambda x: x+1)\nTrue\n```\n\n\n## iter.join\n```python\njoin(iterable, separator='')\n```\n\n将可迭代对象中的每一项连接为字符串。\n\n```python\n>>> from pydu.iter import join\n>>> join([1, '2', 3], separator=',')\n'1,2,3'\n```\n"
  },
  {
    "path": "docs/zh-cn/list.md",
    "content": "# list\n\n提供处理列表的工具。\n\n## list.uniq\n```python\nuniq(seq, key=None)\n```\n\n从列表中删除重复的元素，同时保留其余的顺序。\n\n可选参数 `key` 的值应该是一个函数，它接受一个参数并返回一个 `key` 来测试唯一性。\n\n```python\n>>> from pydu.list import uniq\n>>> uniq([1, 4, 0, 2, 0, 3])\n[1, 4, 0, 2, 3]\n```\n\n\n## list.tolist\n```python\ntolist(obj)\n```\n\n将给定的 `obj` 转换为列表。\n\n如果 `obj` 不是列表，返回 `[obj]`，否则返回 `obj` 本身。\n\n```python\n>>> from pydu.list import tolist\n>>> tolist('foo')\n['foo']\n```\n\n\n## list.flatten\n```python\nflatten(seq)\n```\n\n生成给定 `seq` 中的每个元素。如果元素是可迭代的并且不是字符串，\n就递归 `yield` 元素中的每个子元素。\n\n```python\n>>> from pydu.list import flatten\n>>> flatten([1, [2, [3, 4]]])\n[1, 2, 3, 4]\n```\n"
  },
  {
    "path": "docs/zh-cn/misc.md",
    "content": "# misc\n\n提供诸如 `timeout`、 `trace` 等综合性工具。\n\n## misc.timeout\n```python\ntimeout(seconds)\n```\n\n该函数装饰任何可能会hang住一段时间的函数。参数 `seconds` 应为整数。\n在 `test.py` 中，你可以这么写：\n\n```python\nimport time\nfrom pydu.misc import unix_timeout\n@timeout(1)\ndef f():\ntime.sleep(1.01)\nf()\n```\n\n然后运行 `test.py`，将会看到 `TimeoutError`。\n\n\n## misc.trace\n```python\ntrace(obj)\n```\n\n跟踪运行中程序的每条语句和行号，就像 `bash -x` 。在 `test.py` 中，你可以这么写：\n\n```python\nfrom pydu.misc import trace\n@trace\ndef f():\nprint(1)\na = 1 + 5\nb = [a]\nprint(2)\nf()\n```\n\n然后运行 `test.py`，将会看到如下控制台输出：\n\n```console\ntest.py(4):     print(1)\n1\ntest.py(5):     a = 1 + 5\ntest.py(6):     b = [a]\ntest.py(7):     print(2)\n2\n```\n\n\n## misc.memoize\n```python\nmemoize(obj)\n```\n\n简单的缓存装饰器，可供支持可哈希的位置参数的函数使用。\n它还提供 `cache_clear()` 方法来清除缓存。\n\n```python\n>>> @memoize\n... def foo()\n...     return 1\n...\n>>> foo()\n1\n>>> foo.cache_clear()\n>>>\n```\n\n\n## misc.memoize_when_activated\n```python\nmemoize_when_activated(obj)\n```\n\n缓存装饰器，默认禁用。它能根据需求启用和禁用。\n为效率起见，它只能用于没有参数的类方法。\n\n```python\n>>> class Foo:\n...     @memoize\n...     def foo()\n...         print(1)\n...\n>>> f = Foo()\n>>> # deactivated (default)\n>>> foo()\n1\n>>> foo()\n1\n>>>\n>>> # activated\n>>> foo.cache_activate()\n>>> foo()\n1\n>>> foo()\n>>> foo()\n>>>\n```\n\n\n## misc.super_len\n```python\nsuper_len(obj)\n```\n\n获取具有 `__len__` ， `len` ， `fileno` ， `tell` 等属性的对的长度，\n比如： `list` ， `tuple` ， `dict`， `file` 等等。\n\n```python\n>>> from pydu.misc import super_len\n>>> super_len([1, 2])\n2\n>>> super_len(open('test', 'w'))\n0\n```\n"
  },
  {
    "path": "docs/zh-cn/network.md",
    "content": "# network\n\n提供处理网络的工具。\n\n## network.dotted_netmask\n```python\ndotted_netmask(mask)\n```\n\n将mask从 /`xx` 转化为 `xxx.xxx.xxx.xxx` 形式。\n`mask` 可以是 `int` 或者 `str`。\n\n```python\n>>> from pydu.network import dotted_netmask\n>>> dotted_netmask('24')\n'255.255.255.0'\n>>> dotted_netmask(24)\n'255.255.255.0'\n```\n\n\n## network.private_ipv4s\n\n```python\nprivate_ipv4s\n```\n\nipv4地址列表。每个项是（ipv4地址，掩码）这样的元组。\n\n## network.is_ipv4\n```python\nis_ipv4(ip)\n```\n\n判断给定的 `ip` 是否为 IPV4。\n\n```python\n>>> from pydu.network import is_ipv4\n>>> is_ipv4('8.8.8.8')\nTrue\n>>> is_ipv4('localhost.localdomain')\nFalse\n```\n\n\n## network.is_ipv6\n```python\nis_ipv6(ip)\n```\n\n判断给定的 `ip` 是否为 IPV6。\n\n```python\n>>> from pydu.network import is_ipv6\n>>> is_ipv6('fe80::9e5b:b149:e187:1a18')\nTrue\n>>> is_ipv6('localhost.localdomain')\nFalse\n```\n\n\n## network.get_free_port\n```python\nget_free_port()\n```\n\n获取可以绑定的空闲端口。\n\n```python\n>>> from pydu.network import get_free_port\n>>> get_free_port()\n57118\n```\n\n\n## network.ip2int\n```python\nip2int(ip_str)\n```\n\n将IP转换为整数。支持IPV4和IPV6。如果转换失败，将会抛出 `ValueError`。\n\n```python\n>>> from pydu.network import ip2int\n>>> ip2int('10.1.1.1')\n167837953\n```\n\n\n## network.int2ip\n```python\nint2ip(ip_int)\n```\n\n将整数转换为IP。支持IPV4和IPV6。如果转换失败，将会抛出 `ValueError`。\n\n```python\n>>> from pydu.network import int2ip\n>>> int2ip(167837953)\n'10.1.1.1'\n```\n"
  },
  {
    "path": "docs/zh-cn/path.md",
    "content": "# path\n\n提供处理路径的工具。\n\n## path.cd\n```python\ncd(path)\n```\n\n进入到给定目录的上下文管理器。\n\n```python\n>>> from pydu.path import cd\n>>> with cd('test'):\n...     pass\n```\n\n\n## path.is_super_path\n```python\nis_super_path(path1, path2)\n```\n\n判断 `path1` 是否是 `path2` 的父路径（或父父路径等）。\n注意如果 `path1` 和 `path2` 一样，它也被视作是 `path2` 的父路径。\n\n比如，\\\"/\\\"、\\\"opt\\\"或者\\\"/opt/test\\\"是\\\"/opt/test\\\"的超级父路径，\n而\\\"/opt/t\\\"则不是。\n\n```python\n>>> from pydu.path import is_super_path\n>>> is_super_path('/aa/bb/cc', '/aa/bb/cc')\nTrue\n>>> is_super_path('/aa/bb', '/aa/bb/cc')\nTrue\n>>> is_super_path('/aa/b', '/aa/bb/cc')\nFalse\n```\n\n\n## path.normjoin\n```python\nnormjoin(path)\n```\n\n将一个或多个路径联接，并将之标准化。\n\n```python\n>>> from pydu.path import normjoin\n>>> normjoin('/a', '../b')\n'/b'\n```\n\n\n## path.filename\n```python\nfilename(path)\n```\n\n返回没有扩展名的文件名。\n\n```python\n>>> from pydu.path import filename\n>>> filename('/foo/bar.ext')\n'bar'\n```\n\n\n## path.fileext\n```python\nfileext(path)\n```\n\n返回文件扩展名。\n如果文件没有扩展名，则返回空字符串。\n\n```python\n>>> from pydu.path import fileext\n>>> filename('/foo/bar.ext')\n'.ext'\n```\n"
  },
  {
    "path": "docs/zh-cn/platform.md",
    "content": "# platform\n\n表示特定平台的常量。\n\n## platform.WINDOWS\n\n判断当前操作系统是否为 `WINDOWS` 。\n\n\n## platform.LINUX\n\n判断当前操作系统是否为 `LINUX` 。\n\n\n## platform.POSIX\n\n判断当前操作系统是否为 `POSIX` 。\n\n\n## platform.DARWIN\n\n判断当前操作系统是否为 `DARWIN` 。\n\n\n## platform.SUNOS\n\n判断当前操作系统是否为 `SUNOS` 。\n\n\n## platform.SMARTOS\n\n判断当前操作系统是否为 `SMARTOS` 。\n\n\n## platform.FREEBSD\n\n判断当前操作系统是否为 `FREEBSD` 。\n\n\n## platform.NETBSD\n\n判断当前操作系统是否为 `NETBSD` 。\n\n\n## platform.OPENBSD\n\n判断当前操作系统是否为 `OPENBSD` 。\n\n\n## platform.AIX\n\n判断当前操作系统是否为 `AIX` 。\n"
  },
  {
    "path": "docs/zh-cn/process.md",
    "content": "# process\n\n提供处理进程的工具。\n\n`process` 的实现基于 `psutil`。需要先 `pip install psutil`。\n\n\n## process.get_processes_by_path\n```python\nget_processes_by_path(path)\n```\n\n获取占用给定路径或者其子路径的进程。\n\n```python\n>>> from pydu.process import get_processes_by_path\n>>> get_processes_by_path('/usr/bin/python')\n[{'cmdline': '/usr/bin/python2.7', 'pid': 23383, 'name': 'python'}]\n```\n"
  },
  {
    "path": "docs/zh-cn/request.md",
    "content": "# Request\n\n提供处理请求的工具。\n\n## request.Filename\n\n提供各类获取文件名的方法。\n\n```python\nFilename.from_url(url)\n```\n\n检测文件名为 unicode 或 None。\n\n```python\nFilename.from_headers(headers)\n```\n\n从响应头的Content-Disposition（如果有）中获取文件名。\n`headers` 可以使字典、列表或者字符串。\n\n```python\nFilename.from_any(dst=None, headers=None, url=None)\n```\n\n从目录、响应头部或者路径获取文件名称。\n\n\n## request.download\n```python\nFilename.download(url, dst=None)\n```\n\n将URL下载到当前目录的临时文件中，然后重命名为从URL或者HTTP头中自动检测出的文件名。\n`url` 是要下载的URL地址。\n`dst` 是文件名或目录的目标路径，默认为 `None`，表示下载到当前目录。\n\n\n## request.check_connect\n```python\ncheck_connect(ip, port, retry=1, timeout=0.5)\n```\n\n在给定的 `timeout` 时间内尝试连接给定的 `ip` 和 `port` 。\n\n```python\n>>> from pydu.request import check_connect\n>>> check_connect('http://www.baidu.com', 80)\n'192.168.3.8'\n```\n\n\n## request.update_query_params\n```python\nupdate_query_params(url, params)\n```\n\n更新给定url的查询参数并返回新的url。\n\n```python\n>>> from pydu.request import update_query_params\n>>> update_query_params('http://example.com', {'foo': 1})\n'http://example.com?foo=1'\n```\n\n\n## request.cookies_str_to_dict\n```python\ncookies_str_to_dict(cookies)\n```\n\n将字符串类型的Cookies转换为字典对象。\n\n```python\n>>> from pydu.request import cookies_str_to_dict\n>>> cookies_str_to_dict('a=a;b=b')\n{'a': 'a', 'b': 'b'}\n```\n"
  },
  {
    "path": "docs/zh-cn/set.md",
    "content": "# Set\n\n增强的集合。\n\n## set.OrderedSet\n```python\nOrderedSet(iterable=None)\n```\n\n保持插入元素有序的集合。\n\n```python\n>>> from pydu.set import OrderedSet\n>>> s = OrderedSet([1, 3, 1, 2])\n>>> list(s)\n[1, 3, 2]\n>>> s.discard(3)\n>>> list(s)\n[1, 2]\n```\n"
  },
  {
    "path": "docs/zh-cn/slot.md",
    "content": "# slot\n\n## slot.SlotBase\n```python\nSlotBase(*args, **kwargs)\n```\n\n使用 `__slots__` 的类的基类。当初始化类时未给定位置参数或关键字参数。\n其值会被设置为 `None`。\n\n```python\n>>> from pydu.slot import SlotBase\n>>> class Foo(SlotBase):\n        __slots__ = ('a', 'b', 'c')\n>>> foo = Foo(1, b=2)\n>>> foo.a\n1\n>>> foo.b\n2\n>>> foo.c\n>>>\n```\n"
  },
  {
    "path": "docs/zh-cn/string.md",
    "content": "# String\n\n提供处理字符串的工具。\n\n## string.safeunicode\n```python\nsafeunicode(obj, encoding='utf-8')\n```\n\n将任何对象转换为 `unicode` 字符串。\n\n```python\n>>> from pydu.string import safeunicode\n>>> safeunicode('hello')\nu'hello'\n>>> safeunicode(2)\nu'2'\n>>> safeunicode('\\xe4\\xb8\\xad\\xe6\\x96\\x87')\nu'中文'\n```\n\n\n## string.safeencode\n```python\nsafeencode(obj, encoding='utf-8')\n```\n\n将任何对象转换为编码后字符串（默认为 `utf-8`）。\n\n```python\n>>> from pydu.string import safeencode\n>>> safeencode('hello')\n'hello'\n>>> safeencode(2)\n'2'\n>>> safeencode(u'中文')\n'\\xe4\\xb8\\xad\\xe6\\x96\\x87'\n```\n\n\n## string.lstrips\n```python\nlstrips(text, remove)\n```\n\n移除字符串 `text` 左侧的 `remove`。\n\n```python\n>>> from pydu.string import lstrips\n>>> lstrips('foobar', 'foo')\n'bar'\n>>> lstrips('FOOBARBAZ', ['FOO', 'BAR'])\n'BAZ'\n>>> lstrips('FOOBARBAZ', ['BAR', 'FOO'])\n'BARBAZ'\n```\n\n\n## string.rstrips\n```python\nrstrips(text, remove)\n```\n\n移除字符串 `text` 右侧的 `remove`。\n\n```python\n>>> from pydu.string import rstrips\n>>> rstrips('foobar', 'bar')\n'foo'\n```\n\n\n## string.strips\n```python\nstrips(text, remove)\n```\n\n移除字符串 `text` 两边的 `remove`。\n\n```python\n>>> from pydu.string import strips\n>>> strips('foobarfoo', 'foo')\n'bar'\n```\n\n## string.common_prefix\n```python\ncommon_prefix(l)\n```\n\n返回字符串的共有前缀。\n\n```python\n>>> from pydu.string import common_prefix\n>>> common_prefix(['abcd', 'abc1'])\n'abc'\n```\n\n\n## string.common_suffix\n```python\ncommon_suffix(l)\n```\n\n返回字符串的共有后缀\n\n```python\n>>> from pydu.string import common_suffix\n>>> common_suffix(['dabc', '1abc'])\n'abc'\n```\n\n\n## string.sort\n```python\nsort(s, reversed=False)\n```\n\n对给定的字符串进行排序，默认是升序，如果 `reverse` 的值为 `True`，将以降序排序。\n\n```python\n>>> from pydu.string import sort\n>>> sort('dabc')\n'abcd'\n```\n"
  },
  {
    "path": "docs/zh-cn/system.md",
    "content": "# System\n\n提供处理系统（如追踪文件、创建目录、链接等）的工具。\n\n\n## system.FileTracker\n```python\nFileTracker()\n```\n\n跟踪当前打开的文件，调用 `FileTracker.track()` 开始跟踪。当打开许多文件时，`FileTracker` 能够跟踪它们，你可以通过调用 `FileTracker.get_openfiles()` 来定位得到这些文件对象。\n\n```python\nFiltTracker.track()\n```\n\n开始跟踪打开文件。\n\n```python\nFiltTracker.untrack()\n```\n\n停止跟踪打开文件。\n\n```python\nFiltTracker.get_openfiles()\n```\n\n获取当前已打开的文件。\n\n```python\n>>> from pydu.system import FileTracker\n>>> FileTracker.track()\n>>> f = open('test', 'w')\n>>> FileTracker.get_openfiles()\n{<_io.TextIOWrapper name='test' mode='w' encoding='UTF-8'>}\n>>> f.close()\n>>> FileTracker.get_openfiles()\nset()\n>>> FileTracker.untrack()\n>>> f = open('test', 'w')\n>>> FileTracker.get_openfiles()\nset()\n```\n\n\n## system.makedirs\n```python\nmakedirs(path, mode=0o755, ignore_errors=False, exist_ok=False)\n```\n\n`makedirs` 基于 `os.makedirs` ，它会创建目标文件夹，以及中间文件夹\n（当中间文件夹不存在的时候）。 `mode` 默认值为 `0o75`，当被创建的文件夹已经存在的时候，\n如果 `eist_ok` 的值为 `False`，`makedirs` 将会抛出异常。\n如果 `ignore_errors` 的值为 `True`，所有的异常将会被忽略。\n\n```python\n>>> from pydu.system import makedirs\n>>> makedirs('test1/test2')\n>>> makedirs('test1',exist_ok=True)\n>>> makedirs('test1')\nTraceback (most recent call last):\n...    OSError: Create dir: test1 error.\n```\n\n## system.remove\n```python\nremove(path, mode=0o755, ignore_errors=False, onerror)\n```\n\n删除文件或者文件夹。\n\n如果 `ignore_errors` 的值为 `True` ，异常将会被忽略；\n否者，如果 `onerror` 的值不为 `None` ，那么 `onerror` 函数将会处理这些异常。\n`onerror` 的参数包括 `func` ， `path` 和 `exc_info` 。\n`path` 表示 `func` 函数处理该路径时抛出的异常；\n`exc_info` 是由 `sys.exc_info` 返回的元组。\n如果 `ignore_errors` 为 `False`，并且 `onerror` 的值为 `None`，\n则尝试将只读的 `path` 改为可写并尝试删除，若非只读则抛出异常。\n\n```python\n>>> from pydu.system import makedirs\n>>> from pydu.system import remove\n>>> from pydu.system import touch\n>>> makedirs('test')\n>>> remove('test')\n>>> touch('test.txt')\n>>> remove('test.txt')\n>>> remove('test.txt', ignore_errors=True)\n>>> remove('test.txt')\nTraceback (most recent call last):\n...    OSError: Remove path: test error. Reason: [Errno 2] No such file or directory: 'test.txt'\n```\n\n## system.removes\n```python\nremoves(paths, mode=0o755, ignore_errors=False, onerror)\n```\n\n删除多个文件或者（和）文件夹，其他的参数见 `remove`。\n\n```python\n>>> from pydu.system import makedirs\n>>> from pydu.system import remove\n>>> from pydu.system import open_file\n>>> makedirs('test1')\n>>> makedirs('test2')\n>>> open_file('test.txt')\n>>> removes(['test.txt','test1','test2'])\n```\n\n## system.open_file\n```python\nopen_file(path, mode='wb+', buffer_size=-1, ignore_errors=False):\n```\n\n默认以 `wb+` 的方式打开文件，如果需要被创建的文件的上级目录不存在，该目录将会被创建。如果 `ignore_errors` 为 `True` ，异常将会被忽略。\n\n```python\n>>> from pydu.system import open_file\n>>> open_file('test.txt')\n>>> ls\ntest.txt\n>>> open_file('test1.txt',mode='r')\nTraceback (most recent call last):\n...    OSError: Open file: test1.txt error\n```\n\n## system.copy\n```python\ncopy(src, dst, ignore_errors=False, follow_symlinks=True):\n```\n\n复制源文件（文件夹）到目标文件（文件夹）。当复制的文件夹包含软连接时，\n如果 `symlink` 的值为 `True` ，那么在目标文件夹中会创建相应的软连接；\n否者将会复制软连接所指向的文件。当复制的文件为软连接的时候，\n如果 `symlink` 的值为 `False` ，那么将会创建与软连接指向相同的软连接；\n否者，将会复制软连接所指向的文件。\n\n```python\n>>> from pydu.system import copy,symlink\n>>> from pydu.system import makedirs,open_fle\n>>> open_fle('test/test.txt')\n>>> symlink('test/test.txt','test/test.link')\n>>> copy('test/test.link','test/test_copy1.link')\n>>> copy('test/test.link','test/test_copy2.link',follow_symlink=False)\n```\n\n## system.touch\n```python\ntouch(path):\n```\n\n生成一个新的文件。\n\n```python\n>>> from pydu.system import touch\n>>> touch('test.txt')\n```\n\n## system.symlink\n```python\nsymlink(src, dst, overwrite=False, ignore_errors=False)\n```\n\n创建指向源文件的软连接。\n如果 `overwrite` 的值为 `True` ，那么已存在的软连接将会被覆盖。\n\n```python\n>>> from pydu.system import symlink\n>>> symlink('test.txt','test.link')\n```\n\n!> `symlink` 只支持 `Unix类` 的系统。\n\n## system.link\n```python\nlink(src, dst, overwrite=False, ignore_errors=False):\n```\n\n创建指向源文件的硬连接。\n如果 `overwrite` 的值为 `True` ，那么已存在的硬连接将会被覆盖。\n\n```python\n>>> from pydu.system import link\n>>> link('test.txt','test.link')\n```\n\n!> `link` 只支持 `Unix类` 的系统。\n\n\n## system.which\n```python\nwhich(cmd, mode=os.F_OK | os.X_OK, path=None):\n```\n\n给定命令名称、模式、和环境变量PATH，返回在PATH下符合给定模式的命令的路径，\n如果找不到就返回None。\n\n`mode` 默认是 os.F_OK | os.X_OK。\n`path` 默认是 os.environ.get(\"PATH\")的结果，也可被被自定义的搜索路径重载。\n\n在Python 3中，`which` 就是 `shutil.which`。\n\n```python\n>>> from pydu.system import which\n>>> which('echo')\n/bin/echo\n```\n\n\n## system.chmod\n```python\nchmod(path, mode, recursive=False)\n```\n\n将权限改成给定模式。如果 `recursive` 是True，将会递归。\n\n```python\n>>> from pydu.system import chmod\n>>> chmod('/opt/sometest', 0o744)\n>>> oct(os.stat('/opt/sometest').st_mode)[-3:]\n'744'\n```\n\n!> 尽管Windows支持 `chmod`，但你只能使用它设置文件的只读标志\n（通过 tat.S_IWRITE 和 stat.S_IREAD）常量或者相关整数值。\n其他所有位会被忽略。\n\n\n## system.chcp\n```python\nchcp(code)\n```\n\n设置活动代码页号的上下文管理器。它也能够被当做函数使用。\n\n```python\n>>> from pydu.system import chcp\n>>> chcp(437)\n<active code page number: 437>\n>>> with chcp(437):\n...     pass\n>>>\n```\n\n!> `chcp` 只能用于 `Windows` 系统。\n\n\n## system.preferredencoding\n```python\npreferredencoding(code)\n```\n\n以最佳的方式获取系统编码。\n"
  },
  {
    "path": "docs/zh-cn/unit.md",
    "content": "# Unit\n\n提供处理单位的工具。\n\n## unit.Bytes\n```python\nBytes(bytes)\n```\n\n提供处理字节的各类方法。\n\n```python\nBytes.convert(self, unit=None, multiple=1024)\n```\n\n将字节转化为给定 `unit`。如果 `unit` 是 `None`，将字节转换为合适的单位。\n转换 `multiple` 的默认值是 1024。\n\n```python\n>>> Bytes(1024).convert()\n(1, 'KB')\n```\n"
  },
  {
    "path": "pydu/__init__.py",
    "content": "\"\"\"\nUseful data structures, utils for Python.\n\"\"\"\nfrom __future__ import absolute_import\n\n__version__ = '0.7.2'\n\n\n# Set logging handler to avoid \"No handler found\" warnings.\nimport logging\nfrom logging import NullHandler\n\n\nlogger = logging.getLogger(__name__)\nlogger.addHandler(NullHandler())\n"
  },
  {
    "path": "pydu/archive.py",
    "content": "\"\"\"\nBased on \"python-archive\" -- http://pypi.python.org/pypi/python-archive/\nCopyright (c) 2010 Gary Wilson Jr. <gary.wilson@gmail.com> and contributors.\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\"\"\"\nimport os\nimport shutil\nimport stat\nimport tarfile\nimport zipfile\n\nfrom . import logger\nfrom .compat import string_types\n\n\nclass ArchiveException(Exception):\n    \"\"\"\n    Base exception class for all archive errors.\n    \"\"\"\n\n\nclass UnrecognizedArchiveFormat(ArchiveException):\n    \"\"\"\n    Error raised when passed file is not a recognized archive format.\n    \"\"\"\n\n\ndef extract(path, dst='', ext=''):\n    \"\"\"\n    Unpack the tar or zip file at the specified path or file to the directory\n    specified by to_path.\n    \"\"\"\n    with Archive(path, ext=ext) as archive:\n        archive.extract(dst)\n\n\nclass Archive(object):\n    \"\"\"\n    The external API class that encapsulates an archive implementation.\n    \"\"\"\n    def __init__(self, file, ext=''):\n        \"\"\"\n        Arguments:\n        * 'file' can be a string path to a file or a file-like object.\n        * Optional 'ext' argument can be given to override the file-type\n          guess that is normally performed using the file extension of the\n          given 'file'.  Should start with a dot, e.g. '.tar.gz'.\n        \"\"\"\n        self._archive = self._archive_cls(file, ext=ext)(file)\n\n    @staticmethod\n    def _archive_cls(file, ext=''):\n        \"\"\"\n        Return the proper Archive implementation class, based on the file type.\n        \"\"\"\n        if isinstance(file, string_types):\n            filename = file\n        else:\n            try:\n                filename = file.name\n            except AttributeError:\n                raise UnrecognizedArchiveFormat(\n                    \"File object not a recognized archive format.\")\n        lookup_filename = filename + ext\n        base, tail_ext = os.path.splitext(lookup_filename.lower())\n        cls = extension_map.get(tail_ext)\n        if not cls:\n            base, ext = os.path.splitext(base)\n            cls = extension_map.get(ext)\n        if not cls:\n            raise UnrecognizedArchiveFormat(\n                \"Path not a recognized archive format: %s\" % filename)\n        return cls\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        self.close()\n\n    def extract(self, dst=''):\n        self._archive.extract(dst)\n\n    def list(self):\n        self._archive.list()\n\n    def filenames(self):\n        return self._archive.filenames()\n\n    def close(self):\n        self._archive.close()\n\n\nclass BaseArchive(object):\n    \"\"\"\n    Base Archive class.  Implementations should inherit this class.\n    \"\"\"\n    @staticmethod\n    def _copy_permissions(mode, filename):\n        \"\"\"\n        If the file in the archive has some permissions (this assumes a file\n        won't be writable/executable without being readable), apply those\n        permissions to the unarchived file.\n        \"\"\"\n        if mode & stat.S_IROTH:\n            os.chmod(filename, mode)\n\n    def split_leading_dir(self, path):\n        path = str(path)\n        path = path.lstrip('/').lstrip('\\\\')\n        if '/' in path and (('\\\\' in path and path.find('/') < path.find(\n                '\\\\')) or '\\\\' not in path):\n            return path.split('/', 1)\n        elif '\\\\' in path:\n            return path.split('\\\\', 1)\n        else:\n            return path, ''\n\n    def has_leading_dir(self, paths):\n        \"\"\"\n        Returns true if all the paths have the same leading path name\n        (i.e., everything is in one subdirectory in an archive)\n        \"\"\"\n        common_prefix = None\n        for path in paths:\n            prefix, rest = self.split_leading_dir(path)\n            if not prefix:\n                return False\n            elif common_prefix is None:\n                common_prefix = prefix\n            elif prefix != common_prefix:\n                return False\n        return True\n\n    def extract(self, dst):\n        raise NotImplementedError(\n            'subclasses of BaseArchive must provide an extract() method')\n\n    def list(self):\n        raise NotImplementedError(\n            'subclasses of BaseArchive must provide a list() method')\n\n    def filenames(self):\n        \"\"\"\n        Return a list of the filenames contained in the archive.\n        \"\"\"\n        raise NotImplementedError()\n\n    def __del__(self):\n        if hasattr(self, \"_archive\"):\n            self._archive.close()\n\n\nclass TarArchive(BaseArchive):\n\n    def __init__(self, file):\n        # tarfile's open uses different parameters for file path vs. file obj.\n        if isinstance(file, string_types):\n            self._archive = tarfile.open(name=file)\n        else:\n            self._archive = tarfile.open(fileobj=file)\n\n    def extract(self, dst):\n        members = self._archive.getmembers()\n        leading = self.has_leading_dir(x.name for x in members)\n        for member in members:\n            name = member.name\n            if leading:\n                name = self.split_leading_dir(name)[1]\n            filename = os.path.join(dst, name)\n            if member.isdir():\n                if filename and not os.path.exists(filename):\n                    os.makedirs(filename)\n            else:\n                try:\n                    extracted = self._archive.extractfile(member)\n                except (KeyError, AttributeError) as exc:\n                    # Some corrupt tar files seem to produce this\n                    # (specifically bad symlinks)\n                    logger.error(\"In the tar file %s the member %s is invalid: %s\",\n                                 name, member.name, exc)\n                else:\n                    dirname = os.path.dirname(filename)\n                    if dirname and not os.path.exists(dirname):\n                        os.makedirs(dirname)\n                    with open(filename, 'wb') as outfile:\n                        shutil.copyfileobj(extracted, outfile)\n                        self._copy_permissions(member.mode, filename)\n                finally:\n                    try:\n                        extracted.close()\n                    except NameError:\n                        pass\n\n    def list(self):\n        self._archive.list()\n\n    def filenames(self):\n        return self._archive.getnames()\n\n    def close(self):\n        self._archive.close()\n\n\nclass ZipArchive(BaseArchive):\n\n    def __init__(self, file):\n        # ZipFile's 'file' parameter can be path (string) or file-like obj.\n        self._archive = zipfile.ZipFile(file)\n\n    def extract(self, dst):\n        namelist = self._archive.namelist()\n        leading = self.has_leading_dir(namelist)\n        for name in namelist:\n            data = self._archive.read(name)\n            info = self._archive.getinfo(name)\n            if leading:\n                name = self.split_leading_dir(name)[1]\n            filename = os.path.join(dst, name)\n            dirname = os.path.dirname(filename)\n            if dirname and not os.path.exists(dirname):\n                os.makedirs(dirname)\n            if filename.endswith(('/', '\\\\')):\n                # A directory\n                if not os.path.exists(filename):\n                    os.makedirs(filename)\n            else:\n                with open(filename, 'wb') as outfile:\n                    outfile.write(data)\n                # Convert ZipInfo.external_attr to mode\n                mode = info.external_attr >> 16\n                self._copy_permissions(mode, filename)\n\n    def list(self):\n        self._archive.printdir()\n\n    def filenames(self):\n        return self._archive.namelist()\n\n    def close(self):\n        self._archive.close()\n\n\nextension_map = {\n    '.tar': TarArchive,\n    '.tar.bz2': TarArchive,\n    '.tar.gz': TarArchive,\n    '.tgz': TarArchive,\n    '.tz2': TarArchive,\n    '.zip': ZipArchive,\n}\n"
  },
  {
    "path": "pydu/cmd.py",
    "content": "import os\nimport sys\nimport time\nimport signal\nimport subprocess\nfrom subprocess import Popen, PIPE, STDOUT\n\nfrom .platform import WINDOWS\nfrom .compat import PY2\n\nif PY2:\n    class TimeoutExpired(Exception):\n        \"\"\"\n        This exception is raised when the timeout expires while waiting for a\n        child process.\n\n        Attributes:\n            cmd, output, stdout, stderr, timeout\n        \"\"\"\n        def __init__(self, cmd, timeout, output=None, stderr=None):\n            self.cmd = cmd\n            self.timeout = timeout\n            self.output = output\n            self.stderr = stderr\n\n        def __str__(self):\n            return (\"Command '%s' timed out after %s seconds\" %\n                    (self.cmd, self.timeout))\n\n        @property\n        def stdout(self):\n            return self.output\n\n        @stdout.setter\n        def stdout(self, value):\n            # There's no obvious reason to set this, but allow it anyway so\n            # .stdout is a transparent alias for .output\n            self.output = value\nelse:\n    TimeoutExpired = subprocess.TimeoutExpired\n\n\ndef run(cmd, shell=False, env=None, timeout=None, timeinterval=1):\n    \"\"\"\n    Run cmd based on `subprocess.Popen` and return the tuple of `(returncode, stdout)`.\n    Note, `stderr` is redirected to `stdout`. `shell` is same to parameter of `Popen`.\n    If the process does not terminate after `timeout` seconds, a `TimeoutExpired`\n    exception will be raised. `timeinterval` is workable when timeout is given\n    on Python 2. It means process status checking interval.\n    \"\"\"\n    p = Popen(cmd, shell=shell, stdout=PIPE, stderr=STDOUT, env=env)\n\n    if PY2:\n        if timeout:\n            while timeout > 0 and p.poll() is None:\n                timeout = timeout - timeinterval\n                time.sleep(timeinterval)\n            if p.poll() is None:\n                raise TimeoutExpired(cmd, timeout)\n\n        stdout, _ = p.communicate()\n        return p.poll(), stdout\n    else:\n        stdout, _ = p.communicate(timeout=timeout)\n        return p.poll(), stdout\n\n\ndef run_with_en_env(cmd, shell=False, env=None, timeout=None, timeinterval=1):\n    \"\"\"\n    Run cmd with English character sets environment, so that the output will\n    be in English.\n    Parameters are same with `run`.\n    \"\"\"\n    if WINDOWS:\n        from .system import chcp\n        with chcp(437):\n            return run(cmd, shell=shell,\n                       timeout=timeout, timeinterval=timeinterval)\n    else:\n        env = env if env else os.environ.copy()\n        env.update({'LANG': 'en_US.UTF-8'})\n        return run(cmd, shell=shell, env=env,\n                   timeout=timeout, timeinterval=timeinterval)\n\n\ndef terminate(pid):\n    \"\"\"\n    Terminate process by given pid.\n    On Windows, using Kernel32.TerminateProcess to kill.\n    On Other platforms, using os.kill with signal.SIGTERM to kill.\n    \"\"\"\n    if WINDOWS:\n        # http://code.activestate.com/recipes/347462-terminating-a-subprocess-on-windows/\n        import ctypes\n        PROCESS_TERMINATE = 1\n        handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, pid)\n        ctypes.windll.kernel32.TerminateProcess(handle, -1)\n        ctypes.windll.kernel32.CloseHandle(handle)\n    else:\n        os.kill(pid, signal.SIGTERM)\n\n\nif PY2 and WINDOWS:\n    # enable passing unicode arguments from command line in Python 2.x\n    # https://stackoverflow.com/questions/846850/read-unicode-characters\n    def cmdline_argv():\n        \"\"\"\n        Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode\n        strings.\n\n        Versions 2.x of Python don't support Unicode in sys.argv on Windows,\n        with the underlying Windows API instead replacing multi-byte characters\n        with '?'.\n        \"\"\"\n        from ctypes import POINTER, byref, cdll, c_int, windll\n        from ctypes.wintypes import LPCWSTR, LPWSTR\n\n        GetCommandLineW = cdll.kernel32.GetCommandLineW\n        GetCommandLineW.argtypes = []\n        GetCommandLineW.restype = LPCWSTR\n\n        CommandLineToArgvW = windll.shell32.CommandLineToArgvW\n        CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]\n        CommandLineToArgvW.restype = POINTER(LPWSTR)\n\n        cmd = GetCommandLineW()\n        argc = c_int(0)\n        raw_argv = CommandLineToArgvW(cmd, byref(argc))\n        argnum = argc.value\n        sysnum = len(sys.argv)\n        argv = []\n        if argnum > 0:\n            # Remove Python executable and commands if present\n            start = argnum - sysnum\n            for i in range(start, argnum):\n                argv.append(raw_argv[i])\n        return argv\nelse:\n    def cmdline_argv():\n        return sys.argv\n"
  },
  {
    "path": "pydu/compat.py",
    "content": "\"\"\"Utilities for make the code run both on Python2 and Python3.\n\"\"\"\nimport sys\nimport types\n\nPY2 = sys.version_info[0] == 2\n\n\n# builtins\nif PY2:\n    import __builtin__ as builtins\nelse:\n    import builtins\n\n# url*\nif PY2:\n    import urllib as urlib\n    import urlparse\n    from urlparse import urljoin\n    from urllib import urlencode\nelse:\n    import urllib.request as urlib\n    import urllib.parse as urlparse\n    from urllib.parse import urljoin\n    from urllib.parse import urlencode\n\n# Dictionary iteration\nif PY2:\n    iterkeys = lambda d: d.iterkeys()\n    itervalues = lambda d: d.itervalues()\n    iteritems = lambda d: d.iteritems()\nelse:\n    iterkeys = lambda d: iter(d.keys())\n    itervalues = lambda d: iter(d.values())\n    iteritems = lambda d: iter(d.items())\n\n# string and text types\nif PY2:\n    text_type = unicode\n    string_types = (str, unicode)\n    strbytes_types = (str, unicode, bytes)\n    numeric_types = (int, long)\nelse:\n    text_type = str\n    string_types = (str,)\n    strbytes_types = (str, bytes)\n    numeric_types = (int,)\n\n# imap, izip\nif PY2:\n    from itertools import imap, izip\nelse:\n    imap = map\n    izip = zip\n\nif PY2:\n    reduce = reduce\nelse:\n    from functools import reduce\n\n# next\nif PY2:\n    has_next_attr = lambda x: x and hasattr(x, 'next')\nelse:\n    has_next_attr = lambda x: x and hasattr(x, '__next__')\n\n# Class Type\nif PY2:\n    ClassTypes = (type, types.ClassType)\nelse:\n    ClassTypes = (type,)\n\n# cmp\nif PY2:\n    cmp = cmp\nelse:\n    cmp = lambda x, y: (x > y) - (x < y)\n\n\n# is iterable\ndef is_iterable(x):\n    \"\"\"An implementation independent way of checking for iterables.\"\"\"\n    try:\n        iter(x)\n    except TypeError:\n        return False\n    else:\n        return True\n"
  },
  {
    "path": "pydu/console.py",
    "content": "import sys\nimport shutil\n\nfrom .platform import WINDOWS, POSIX\n\n\nif hasattr(shutil, 'get_terminal_size'):\n    # Actually (80, 25) is the default size used by many terminal emulators\n    def console_size(fallback=(80, 25)):\n        return shutil.get_terminal_size(fallback=fallback)\nelse:\n    # http://bitbucket.org/techtonik/python-pager\n    def console_size(fallback=(80, 25)):\n        \"\"\"\n        For Windows, return (width, height) of available window area, fallback\n        if no console is allocated.\n        For POSIX system, return (width, height) of console terminal, fallback\n        on IOError, i.e. when no console is allocated.\n        For other system, return fallback.\n        Fallback defaults to (80, 25) which is the default size used by many\n        terminal emulators.\n        \"\"\"\n\n        if WINDOWS:\n            STD_OUTPUT_HANDLE = -11\n\n            # get console handle\n            from ctypes import windll, Structure, byref\n            try:\n                from ctypes.wintypes import SHORT, WORD, DWORD\n            except ImportError:\n                from ctypes import (\n                    c_short as SHORT, c_ushort as WORD, c_ulong as DWORD)\n            console_handle = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)\n\n            # CONSOLE_SCREEN_BUFFER_INFO Structure\n            class COORD(Structure):\n                _fields_ = [('X', SHORT), ('Y', SHORT)]\n\n            class SMALL_RECT(Structure):\n                _fields_ = [('Left', SHORT), ('Top', SHORT),\n                            ('Right', SHORT), ('Bottom', SHORT)]\n\n            class CONSOLE_SCREEN_BUFFER_INFO(Structure):\n                _fields_ = [('dwSize', COORD),\n                            ('dwCursorPosition', COORD),\n                            ('wAttributes', WORD),\n                            ('srWindow', SMALL_RECT),\n                            ('dwMaximumWindowSize', DWORD)]\n\n            sbi = CONSOLE_SCREEN_BUFFER_INFO()\n            ret = windll.kernel32.GetConsoleScreenBufferInfo(\n                console_handle, byref(sbi))\n            if ret == 0:\n                return fallback\n            return ((sbi.srWindow.Right - sbi.srWindow.Left + 1) or fallback[0],\n                    (sbi.srWindow.Bottom - sbi.srWindow.Top + 1) or fallback[1])\n\n        elif POSIX:\n            # http://www.kernel.org/doc/man-pages/online/pages/man4/tty_ioctl.4.html\n            from fcntl import ioctl\n            from termios import TIOCGWINSZ\n            from array import array\n\n            \"\"\"\n            struct winsize {\n                unsigned short ws_row;\n                unsigned short ws_col;\n                unsigned short ws_xpixel;   /* unused */\n                unsigned short ws_ypixel;   /* unused */\n            };\n            \"\"\"\n            winsize = array(\"H\", [0] * 4)\n            try:\n                ioctl(sys.stdout.fileno(), TIOCGWINSZ, winsize)\n            except IOError:\n                # for example IOError: [Errno 25] Inappropriate ioctl for device\n                # when output is redirected\n                pass\n            return winsize[1] or fallback[0], winsize[0] or fallback[1]\n\n        return fallback\n"
  },
  {
    "path": "pydu/convert.py",
    "content": "import functools\nfrom pydu.compat import PY2\n\n\ndef boolean(obj):\n    \"\"\"\n    Convert obj to a boolean value.\n    If obj is string, obj will converted by case-insensitive way:\n        * convert `yes`, `y`, `on`, `true`, `t`, `1` to True\n        * convert `no`, `n`, `off`, `false`, `f`, `0` to False\n        * raising TypeError if other values passed\n    If obj is non-string, obj will converted by bool(obj).\n    \"\"\"\n\n    try:\n        text = obj.strip().lower()\n    except AttributeError:\n        return bool(obj)\n\n    if text in ('yes', 'y', 'on', 'true', 't', '1'):\n        return True\n    elif text in ('no', 'n', 'off', 'false', 'f', '0'):\n        return False\n    else:\n        raise ValueError(\"Unable to convert {!r} to a boolean value.\".format(text))\n\n\n##########################################################################\n# Convert number from one base(2, 8, 10, 16) to another base(2, 8, 10, 16)\n##########################################################################\n_oct_index = 1 if PY2 else 2\n\n\ndef _rstrip_L(func):\n    if PY2:\n        @functools.wraps(func)\n        def wrapper(x):\n            return func(x).rstrip('L')\n        return wrapper\n    return func\n\n\n# binary to octal, decimal and hexadecimal\n@_rstrip_L\ndef bin2oct(x):\n    \"\"\"\n    Convert binary string to octal string.\n    For instance: '1001' -> '11'\n    \"\"\"\n    return oct(int(x, 2))[_oct_index:]\n\n\ndef bin2dec(x):\n    \"\"\"\n    Convert binary string to decimal number.\n    For instance: '11' -> 3\n    \"\"\"\n    return int(x, 2)\n\n\n@_rstrip_L\ndef bin2hex(x):\n    \"\"\"\n    Convert binary string to hexadecimal string.\n    For instance: '11010' -> '1a'\n    \"\"\"\n    return hex(int(x, 2))[2:]\n\n\n# octal to binary, decimal and hexadecimal\n@_rstrip_L\ndef oct2bin(x):\n    \"\"\"\n    Convert octal string to binary string.\n    For instance: '11' -> '1001'\n    \"\"\"\n    return bin(int(x, 8))[2:]\n\n\ndef oct2dec(x):\n    \"\"\"\n    Convert octal string to decimal number.\n    For instance: '11' -> 9\n    \"\"\"\n    return int(x, 8)\n\n\n@_rstrip_L\ndef oct2hex(x):\n    \"\"\"\n    Convert octal string to hexadecimal string.\n    For instance: '32' -> '1a'\n    \"\"\"\n    return hex(int(x, 8))[2:]\n\n\n# decimal to binary, octal and hexadecimal\n@_rstrip_L\ndef dec2bin(x):\n    \"\"\"\n    Convert decimal number to binary string.\n    For instance: 3 -> '11'\n    \"\"\"\n    return bin(x)[2:]\n\n\n@_rstrip_L\ndef dec2oct(x):\n    \"\"\"\n    Convert decimal number to octal string.\n    For instance: 9 -> '11'\n    \"\"\"\n    return oct(x)[_oct_index:]\n\n\n@_rstrip_L\ndef dec2hex(x):\n    \"\"\"\n    Convert decimal number to hexadecimal string.\n    For instance: 26 -> '1a'\n    \"\"\"\n    return hex(x)[2:]\n\n\n# hexadecimal to binary, octal and decimal\n@_rstrip_L\ndef hex2bin(x):\n    \"\"\"\n    Convert hexadecimal string to binary string.\n    For instance: '1a' -> '11010'\n    \"\"\"\n    return bin(int(x, 16))[2:]\n\n\n@_rstrip_L\ndef hex2oct(x):\n    \"\"\"\n    Convert hexadecimal string to octal string.\n    For instance: '1a' -> '32'\n    \"\"\"\n    return oct(int(x, 16))[_oct_index:]\n\n\ndef hex2dec(x):\n    \"\"\"\n    Convert hexadecimal string to decimal number.\n    For instance: '1a' -> 26\n    \"\"\"\n    return int(x, 16)\n"
  },
  {
    "path": "pydu/dict.py",
    "content": "# coding: utf-8\nimport collections\n\ntry:\n    # Python 3\n    from collections.abc import Callable, Mapping, MutableMapping\nexcept ImportError:\n    # Python 2.7\n    from collections import Callable, Mapping, MutableMapping\n\nfrom .compat import PY2\n\n\nclass AttrDict(dict):\n    \"\"\"\n    A AttrDict object is like a dictionary except `obj.foo` can be used\n    in addition to `obj['foo']`.\n\n        >>> o = AttrDict(a=1)\n        >>> o.a\n        1\n        >>> o['a']\n        1\n        >>> o.a = 2\n        >>> o['a']\n        2\n        >>> del o.a\n        >>> o.a\n        Traceback (most recent call last):\n            ...\n        AttributeError: 'a'\n\n    \"\"\"\n\n    def __getattr__(self, key):\n        try:\n            return self[key]\n        except KeyError as k:\n            raise AttributeError(k)\n\n    def __setattr__(self, key, value):\n        self[key] = value\n\n    def __delattr__(self, key):\n        try:\n            del self[key]\n        except KeyError as k:\n            raise AttributeError(k)\n\n    def __repr__(self):\n        return '<AttrDict ' + dict.__repr__(self) + '>'\n\n\nclass CaseInsensitiveDict(MutableMapping):\n    \"\"\"\n    A case-insensitive ``dict``-like object.\n    Implements all methods and operations of\n    ``MutableMapping`` as well as dict's ``copy``. Also\n    provides ``lower_items``.\n    All keys are expected to be strings. The structure remembers the\n    case of the last key to be set, and ``iter(instance)``,\n    ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()``\n    will contain case-sensitive keys. However, querying and contains\n    testing is case insensitive:\n        cid = CaseInsensitiveDict()\n        cid['Accept'] = 'application/json'\n        cid['aCCEPT'] == 'application/json'  # True\n        list(cid) == ['Accept']  # True\n    For example, ``headers['content-encoding']`` will return the\n    value of a ``'Content-Encoding'`` response header, regardless\n    of how the header name was originally stored.\n    If the constructor, ``.update``, or equality comparison\n    operations are given keys that have equal ``.lower()``s, the\n    behavior is undefined.\n    \"\"\"\n\n    def __init__(self, data=None, **kwargs):\n        self._store = {}\n        if data is None:\n            data = {}\n        self.update(data, **kwargs)\n\n    def __setitem__(self, key, value):\n        # Use the lowercased key for lookups, but store the actual\n        # key alongside the value.\n        self._store[key.lower()] = (key, value)\n\n    def __getitem__(self, key):\n        return self._store[key.lower()][1]\n\n    def __delitem__(self, key):\n        del self._store[key.lower()]\n\n    def __iter__(self):\n        return (casedkey for casedkey, mappedvalue in self._store.values())\n\n    def __len__(self):\n        return len(self._store)\n\n    def lower_items(self):\n        \"\"\"Like iteritems(), but with all lowercase keys.\"\"\"\n        return (\n            (lowerkey, keyval[1])\n            for (lowerkey, keyval)\n            in self._store.items()\n        )\n\n    def __eq__(self, other):\n        if isinstance(other, Mapping):\n            other = CaseInsensitiveDict(other)\n        else:\n            return NotImplemented\n        # Compare insensitively\n        return dict(self.lower_items()) == dict(other.lower_items())\n\n    # Copy is required\n    def copy(self):\n        return CaseInsensitiveDict(self._store.values())\n\n    def __repr__(self):\n        return '%s(%r)' % (self.__class__.__name__, dict(self.items()))\n\n\nclass LookupDict(dict):\n    \"\"\"\n    Dictionary lookup object.\n        d = LookupDict()\n        print(d['key'])  # None\n        d['key'] = 1\n        print(d['key'])  # 1\n    \"\"\"\n\n    def __init__(self, name=None):\n        self.name = name\n        super(LookupDict, self).__init__()\n\n    def __getitem__(self, key):\n        # We allow fall-through here, so values default to None\n        return self.get(key, None)\n\n\n# https://stackoverflow.com/questions/6190331/can-i-do-an-ordered-default-dict-in-python\nclass OrderedDefaultDict(collections.OrderedDict):\n    \"\"\"\n    Dictionary that remembers insertion order and has default value\n    with default factory.\n\n    The default factory is called without arguments to produce\n    a new value when a key is not present, in `__getitem__` only.\n    An `OrderedDefaultDict` compares equal to a `collections.defaultdict`\n    with the same items. All remaining arguments are treated the same\n    as if they were passed to the `defaultdict` constructor,\n    including keyword arguments.\n    \"\"\"\n\n    def __init__(self, default_factory=None, *args, **kwds):\n        if (default_factory is not None and\n                not isinstance(default_factory, Callable)):\n            raise TypeError('First argument must be callable')\n        super(OrderedDefaultDict, self).__init__(*args, **kwds)\n        self.default_factory = default_factory\n\n    def __getitem__(self, key):\n        try:\n            return super(OrderedDefaultDict, self).__getitem__(key)\n        except KeyError:\n            return self.__missing__(key)\n\n    def __missing__(self, key):\n        if self.default_factory is None:\n            raise KeyError(key)\n        self[key] = value = self.default_factory()\n        return value\n\n    def __reduce__(self):\n        if self.default_factory is None:\n            args = tuple()\n        else:\n            args = self.default_factory,\n        return type(self), args, None, None, self.items()\n\n    def copy(self):\n        return self.__copy__()\n\n    def __copy__(self):\n        return self.__class__(self.default_factory, self)\n\n    if PY2:\n        def __deepcopy__(self, memo):\n            import copy\n            return self.__class__(self.default_factory, copy.deepcopy(self.items()))\n    else:\n        def __deepcopy__(self, memo):\n            import copy\n            return self.__class__(self.default_factory, copy.deepcopy(iter(self.items())))\n\n    def __repr__(self):\n        return 'OrderedDefaultDict({default_factory}, {repr})'.format(\n            default_factory=self.default_factory,\n            repr=super(OrderedDefaultDict, self).__repr__()\n        )\n\n\ndef attrify(obj):\n    if isinstance(obj, list):\n        for i, v in enumerate(obj):\n            obj[i] = attrify(v)\n        return obj\n    elif isinstance(obj, dict):\n        attrd = AttrDict()\n        for key, value in obj.items():\n            value = attrify(value)\n            setattr(attrd, key, value)\n        return attrd\n    else:\n        return obj\n"
  },
  {
    "path": "pydu/dt.py",
    "content": "import time\n\n\nclass timer(object):\n    \"\"\"\n    A timer can time how long does calling take as a context manager or decorator.\n    If assign ``print_func`` with ``sys.stdout.write``, ``logger.info`` and so on,\n    timer will print the spent time.\n    \"\"\"\n\n    def __init__(self, print_func=None):\n        self.elapsed = None\n        self.print_func = print_func\n\n    def __enter__(self):\n        self.start = time.time()\n\n    def __exit__(self, *_):\n        self.elapsed = time.time() - self.start\n        if self.print_func:\n            self.print_func(self.__str__())\n\n    def __call__(self, fun):\n        def wrapper(*args, **kwargs):\n            with self:\n                return fun(*args, **kwargs)\n        return wrapper\n\n    def __str__(self):\n        return 'Spent time: {}s'.format(self.elapsed)\n"
  },
  {
    "path": "pydu/environ.py",
    "content": "import os\nfrom contextlib import contextmanager\nfrom pydu.list import tolist\nfrom pydu.compat import iteritems\n\n\n@contextmanager\ndef environ(**kwargs):\n    \"\"\"\n    Context manager for updating one or more environment variables.\n\n    Preserves the previous environment variable (if available) and\n    recovers when exiting the context manager.\n\n    If given variable_name=None, it means removing the variable from\n    environment temporarily.\n    \"\"\"\n    original_kwargs = {}\n\n    for key in kwargs:\n        original_kwargs[key] = os.environ.get(key, None)\n        if kwargs[key] is None and original_kwargs[key] is not None:\n            del os.environ[key]\n        elif kwargs[key] is not None:\n            os.environ[key] = kwargs[key]\n\n    yield\n\n    for key, value in iteritems(original_kwargs):\n        if value is None:\n            os.environ.pop(key, None)\n            continue\n\n        os.environ[key] = value\n\n\n@contextmanager\ndef path(append=None, prepend=None, replace=None):\n    \"\"\"\n    Context manager for updating the PATH environment variable which\n    appends, prepends or replaces the PATH with given string or\n    a list of strings.\n    \"\"\"\n    original = os.environ['PATH']\n\n    if replace:\n        replace = tolist(replace)\n        os.environ['PATH'] = os.pathsep.join(replace)\n    else:\n        if append:\n            append = tolist(append)\n            append.insert(0, os.environ['PATH'])\n            os.environ['PATH'] = os.pathsep.join(append)\n\n        if prepend:\n            prepend = tolist(prepend)\n            prepend.append(os.environ['PATH'])\n            os.environ['PATH'] = os.pathsep.join(prepend)\n\n    yield\n\n    os.environ['PATH'] = original\n"
  },
  {
    "path": "pydu/exception.py",
    "content": "import contextlib\nfrom functools import wraps\nfrom .compat import PY2\n\n\nif PY2:\n    @contextlib.contextmanager\n    def ignore(*exceptions):\n        try:\n            yield\n        except exceptions:\n            pass\nelse:\n    ignore = contextlib.suppress\n\n\ndef default_if_except(exception_clses, default=None):\n    \"\"\"\n    A exception decorator which excepts given exceptions and\n    return default value.\n    \"\"\"\n    def _default_if_except(func):\n        @wraps(func)\n        def wrapper(*args, **kwargs):\n            try:\n                return func(*args, **kwargs)\n            except exception_clses:\n                return default\n        return wrapper\n    return _default_if_except\n"
  },
  {
    "path": "pydu/functional.py",
    "content": "from .compat import reduce\n\n\ndef compose(*funcs):\n    \"\"\"\n    Compose all functions. The previous function must accept one argument,\n    which is the output of the next function. The last function can accept\n    any args and kwargs.\n\n    compose(f1, f2, f3)(*x) is same to f1(f2(f3(*x))).\n    \"\"\"\n    return reduce(\n        lambda f1, f2: (lambda *args, **kwargs: f2(f1(*args, **kwargs))),\n        reversed(funcs))\n"
  },
  {
    "path": "pydu/inspect.py",
    "content": "from __future__ import absolute_import\n\nimport inspect\n\nfrom .compat import PY2\n\n\ndef getargspec(func):\n    \"\"\"\n    Get the names and default values of a function's parameters.\n\n    A tuple of four things is returned: (args, varargs, keywords, defaults).\n    'args' is a list of the argument names, including keyword-only argument names.\n    'varargs' and 'keywords' are the names of the * and ** parameters or None.\n    'defaults' is an n-tuple of the default values of the last n parameters.\n    \"\"\"\n    if PY2:\n        return inspect.getargspec(func)\n    else:\n        sig = inspect.signature(func)\n        args = [\n            p.name for p in sig.parameters.values()\n            if p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD\n        ]\n        varargs = [\n            p.name for p in sig.parameters.values()\n            if p.kind == inspect.Parameter.VAR_POSITIONAL\n        ]\n        varargs = varargs[0] if varargs else None\n        varkw = [\n            p.name for p in sig.parameters.values()\n            if p.kind == inspect.Parameter.VAR_KEYWORD\n        ]\n        varkw = varkw[0] if varkw else None\n        defaults = [\n                       p.default for p in sig.parameters.values()\n                       if\n                       p.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD and p.default is not p.empty\n                   ] or None\n        return args, varargs, varkw, defaults\n\n\ndef get_func_args(func):\n    \"\"\"\n    Return a list of the argument names. Arguments such as\n    *args and **kwargs are not included.\n    \"\"\"\n    if PY2:\n        argspec = inspect.getargspec(func)\n        if inspect.ismethod(func):\n            return argspec.args[1:] # ignore 'self'\n        return argspec.args\n    else:\n        sig = inspect.signature(func)\n        return [\n            name for name, param in sig.parameters.items()\n            if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD and name != 'self'\n        ]\n\n\ndef get_func_full_args(func):\n    \"\"\"\n    Return a list of (argument name, default value) tuples. If the argument\n    does not have a default value, omit it in the tuple. Arguments such as\n    *args and **kwargs are also included.\n    \"\"\"\n    if PY2:\n        argspec = inspect.getargspec(func)\n        if inspect.ismethod(func):\n            args = argspec.args[1:] # ignore 'self'\n        else:\n            args = argspec.args\n        defaults = argspec.defaults or []\n        # Split args into two lists depending on whether they have default value\n        no_default = args[:len(args) - len(defaults)]\n        with_default = args[len(args) - len(defaults):]\n        # Join the two lists and combine it with default values\n        args = [(arg,) for arg in no_default] + zip(with_default, defaults)\n        # Add possible *args and **kwargs and prepend them with '*' or '**'\n        varargs = [('*' + argspec.varargs,)] if argspec.varargs else []\n        kwargs = [('**' + argspec.keywords,)] if argspec.keywords else []\n        return args + varargs + kwargs\n    else:\n        sig = inspect.signature(func)\n        args = []\n        for arg_name, param in sig.parameters.items():\n            name = arg_name\n            # Ignore 'self'\n            if name == 'self':\n                continue\n            if param.kind == inspect.Parameter.VAR_POSITIONAL:\n                name = '*' + name\n            elif param.kind == inspect.Parameter.VAR_KEYWORD:\n                name = '**' + name\n            if param.default != inspect.Parameter.empty:\n                args.append((name, param.default))\n            else:\n                args.append((name,))\n        return args\n\n\ndef func_accepts_kwargs(func):\n    \"\"\"\n    Check whether or not the func accepts kwargs.\n    \"\"\"\n    # Not all callables are inspectable with getargspec, so we'll\n    # try a couple different ways but in the end fall back on assuming\n    # it is -- we don't want to prevent registration of valid but weird\n    # callables.\n    if PY2:\n        try:\n            argspec = inspect.getargspec(func)\n        except TypeError:\n            try:\n                argspec = inspect.getargspec(func.__call__)\n            except (TypeError, AttributeError):\n                argspec = None\n        return not argspec or argspec[2] is not None\n    else:\n        return any(\n            p for p in inspect.signature(func).parameters.values()\n            if p.kind == p.VAR_KEYWORD\n        )\n\n\ndef func_accepts_var_args(func):\n    \"\"\"\n    Check whether or not the func accepts var args.\n    \"\"\"\n    if PY2:\n        return inspect.getargspec(func)[1] is not None\n    else:\n        return any(\n            p for p in inspect.signature(func).parameters.values()\n            if p.kind == p.VAR_POSITIONAL\n        )\n\n\ndef func_supports_parameter(func, parameter):\n    \"\"\"\n    Check whether or the func supports the given parameter.\n    \"\"\"\n    if PY2:\n        args, varargs, varkw, defaults = inspect.getargspec(func)\n        if inspect.ismethod(func):\n            args = args[1:] # ignore 'self'\n        return parameter in args + [varargs, varkw]\n    else:\n        parameters = [name for name in inspect.signature(func).parameters if name != 'self']\n        return parameter in parameters\n\n\ndef func_has_no_args(func):\n    \"\"\"\n    Check whether or not the func has any args.\n    \"\"\"\n    args = inspect.getargspec(func)[0] if PY2 else [\n        p for name, p in inspect.signature(func).parameters.items()\n        if p.kind == p.POSITIONAL_OR_KEYWORD and name != 'self'\n    ]\n    return len(args) == 1\n"
  },
  {
    "path": "pydu/iter.py",
    "content": "\"\"\"iteration tools\"\"\"\nfrom .compat import builtins, imap\n\n\ndef first(iterable):\n    \"\"\"\n    Get the first item in the iterable.\n    \"\"\"\n    return next(iter(iterable))\n\n\ndef last(iterable):\n    \"\"\"\n    Get the last item in the iterable.\n    Warning, this can be slow due to iter step by step to last one.\n    \"\"\"\n    item = None\n    for item in iterable:\n        pass\n    return item\n\n\ndef all(iterable, predicate):\n    \"\"\"\n    Returns True if all elements in the given iterable are True for the\n    given predicate function.\n    \"\"\"\n    return builtins.all(predicate(x) for x in iterable)\n\n\ndef any(iterable, predicate):\n    \"\"\"\n    Returns True if any element in the given iterable is True for the\n    given predicate function.\n    \"\"\"\n    return builtins.any(predicate(x) for x in iterable)\n\n\ndef join(iterable, separator=''):\n    \"\"\"\n    Join each item of iterable to string.\n    \"\"\"\n    return separator.join(imap(str, iterable))\n"
  },
  {
    "path": "pydu/list.py",
    "content": "\ntry:\n    # Python 3\n    from collections.abc import Iterable\nexcept ImportError:\n    # Python 2.7\n    from collections import Iterable\n\nfrom pydu.compat import strbytes_types\n\n\ndef uniq(seq, key=None):\n    \"\"\"\n    Removes duplicate elements from a list while preserving the order of the rest.\n\n    The value of the optional `key` parameter should be a function that\n    takes a single argument and returns a key to test the uniqueness.\n    \"\"\"\n    key = key or (lambda x: x)\n    seen = set()\n    uniq_list = []\n    for value in seq:\n        uniq_value = key(value)\n        if uniq_value in seen:\n            continue\n        seen.add(uniq_value)\n        uniq_list.append(value)\n    return uniq_list\n\n\ndef tolist(obj):\n    \"\"\"\n    Convert given `obj` to list.\n\n    If `obj` is not a list, return `[obj]`, else return `obj` itself.\n    \"\"\"\n    if not isinstance(obj, list):\n        return [obj]\n    return obj\n\n\n# https://stackoverflow.com/questions/2158395/flatten-an-irregular-list-of-lists\ndef flatten(seq):\n    \"\"\"\n    Generate each element of the given `seq`. If the element is iterable and\n    is not string, it yields each sub-element of the element recursively.\n    \"\"\"\n    for element in seq:\n        if isinstance(element, Iterable) and \\\n                not isinstance(element, strbytes_types):\n            for sub in flatten(element):\n                yield sub\n        else:\n            yield element\n"
  },
  {
    "path": "pydu/misc.py",
    "content": "import os\nimport sys\nimport linecache\nimport functools\nimport io\nfrom threading import Thread\n\nfrom . import logger\n\n\nclass TimeoutError(Exception):\n    pass\n\n\ndef timeout(seconds, error_message='Time out'):\n    def decorated(func):\n\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            share = [TimeoutError(error_message)]\n\n            def func_with_except():\n                try:\n                    share[0] = func(*args, **kwargs)\n                except Exception as e:\n                    share[0] = e\n\n            t = Thread(target=func_with_except)\n            t.daemon = True\n            try:\n                t.start()\n                t.join(seconds)\n            except Exception as e:\n                logger.error('Starting timeout thread for %s error', e)\n                raise e\n            result = share[0]\n            if isinstance(result, BaseException):\n                raise result\n            return result\n\n        return wrapper\n\n    return decorated\n\n\ndef trace(func):  # pragma: no cover\n    def globaltrace(frame, why, arg):\n        if why == 'call':\n            return localtrace\n        return None\n\n    def localtrace(frame, why, arg):\n        if why == 'line':\n            # record the file name and line number of every trace\n            filename = frame.f_code.co_filename\n            lineno = frame.f_lineno\n            bname = os.path.basename(filename)\n            print('{}({}): {}\\n'.format(\n                bname,\n                lineno,\n                linecache.getline(filename, lineno).strip('\\r\\n')))\n        return localtrace\n\n    def _func(*args, **kwds):\n        try:\n            sys.settrace(globaltrace)\n            result = func(*args, **kwds)\n            return result\n        finally:\n            sys.settrace(None)\n\n    return _func\n\n\n# https://github.com/giampaolo/psutil/blob/master/psutil/_common.py\ndef memoize(func):\n    \"\"\"\n    A simple memoize decorator for functions supporting (hashable)\n    positional arguments.\n    It also provides a cache_clear() function for clearing the cache:\n\n    >>> @memoize\n    ... def foo()\n    ...     return 1\n        ...\n    >>> foo()\n    1\n    >>> foo.cache_clear()\n    >>>\n    \"\"\"\n    @functools.wraps(func)\n    def wrapper(*args, **kwargs):\n        key = (args, frozenset(sorted(kwargs.items())))\n        try:\n            return cache[key]\n        except KeyError:\n            ret = cache[key] = func(*args, **kwargs)\n            return ret\n\n    def cache_clear():\n        \"\"\"Clear cache.\"\"\"\n        cache.clear()\n\n    cache = {}\n    wrapper.cache_clear = cache_clear\n    return wrapper\n\n\n# https://github.com/giampaolo/psutil/blob/master/psutil/_common.py\ndef memoize_when_activated(func):\n    \"\"\"\n    A memoize decorator which is disabled by default. It can be\n    activated and deactivated on request.\n    For efficiency reasons it can be used only against class methods\n    accepting no arguments.\n\n    >>> class Foo:\n    ...     @memoize\n    ...     def foo(self)\n    ...         print(1)\n    ...\n    >>> f = Foo()\n    >>> # deactivated (default)\n    >>> foo()\n    1\n    >>> foo()\n    1\n    >>>\n    >>> # activated\n    >>> foo.cache_activate()\n    >>> foo()\n    1\n    >>> foo()\n    >>> foo()\n    >>>\n    \"\"\"\n    @functools.wraps(func)\n    def wrapper(self):\n        if not wrapper.cache_activated:\n            return func(self)\n        else:\n            try:\n                ret = cache[func]\n            except KeyError:\n                ret = cache[func] = func(self)\n            return ret\n\n    def cache_activate():\n        \"\"\"Activate cache.\"\"\"\n        wrapper.cache_activated = True\n\n    def cache_deactivate():\n        \"\"\"Deactivate and clear cache.\"\"\"\n        wrapper.cache_activated = False\n        cache.clear()\n\n    cache = {}\n    wrapper.cache_activated = False\n    wrapper.cache_activate = cache_activate\n    wrapper.cache_deactivate = cache_deactivate\n    return wrapper\n\n\n# https://github.com/requests/requests/blob/master/requests/utils.py\ndef super_len(obj):\n    total_length = None\n    current_position = 0\n\n    if hasattr(obj, '__len__'):\n        total_length = len(obj)\n\n    elif hasattr(obj, 'len'):\n        total_length = obj.len\n\n    elif hasattr(obj, 'fileno'):\n        try:\n            fileno = obj.fileno()\n        except io.UnsupportedOperation:\n            pass\n        else:\n            total_length = os.fstat(fileno).st_size\n\n    if hasattr(obj, 'tell'):\n        try:\n            current_position = obj.tell()\n        except (OSError, IOError):\n            # This can happen in some weird situations, such as when the file\n            # is actually a special file descriptor like stdin. In this\n            # instance, we don't know what the length is, so set it to zero and\n            # let requests chunk it instead.\n            if total_length is not None:\n                current_position = total_length\n        else:\n            if hasattr(obj, 'seek') and total_length is None:\n                # StringIO and BytesIO have seek but no useable fileno\n                try:\n                    # seek to end of file\n                    obj.seek(0, 2)\n                    total_length = obj.tell()\n\n                    # seek back to current position to support\n                    # partially read file-like objects\n                    obj.seek(current_position or 0)\n                except (OSError, IOError):\n                    total_length = 0\n\n    if total_length is None:\n        total_length = 0\n\n    return max(0, total_length - current_position)\n"
  },
  {
    "path": "pydu/network.py",
    "content": "import socket\nimport struct\nimport ctypes\nimport binascii\nfrom contextlib import closing\n\nfrom .platform import WINDOWS\nfrom .string import safeencode, safeunicode\nfrom .convert import hex2dec, dec2hex\n\n\n# https://github.com/hickeroar/win_inet_pton/blob/master/win_inet_pton.py\nif WINDOWS:\n    class _sockaddr(ctypes.Structure):\n        _fields_ = [(\"sa_family\", ctypes.c_short),\n                    (\"__pad1\", ctypes.c_ushort),\n                    (\"ipv4_addr\", ctypes.c_byte * 4),\n                    (\"ipv6_addr\", ctypes.c_byte * 16),\n                    (\"__pad2\", ctypes.c_ulong)]\n\n\n    WSAStringToAddressA = ctypes.windll.ws2_32.WSAStringToAddressA\n    WSAAddressToStringA = ctypes.windll.ws2_32.WSAAddressToStringA\n\n\n    def _win_inet_pton(address_family, ip_str):\n        ip_str = safeencode(ip_str)\n        addr = _sockaddr()\n        addr.sa_family = address_family\n        addr_size = ctypes.c_int(ctypes.sizeof(addr))\n\n        if WSAStringToAddressA(\n                ip_str,\n                address_family,\n                None,\n                ctypes.byref(addr),\n                ctypes.byref(addr_size)\n        ) != 0:\n            raise socket.error(ctypes.FormatError())\n\n        if address_family == socket.AF_INET:\n            return ctypes.string_at(addr.ipv4_addr, 4)\n        if address_family == socket.AF_INET6:\n            return ctypes.string_at(addr.ipv6_addr, 16)\n\n        raise socket.error('unknown address family')\n\n\n    def _win_inet_ntop(address_family, packed_ip):\n        addr = _sockaddr()\n        addr.sa_family = address_family\n        addr_size = ctypes.c_int(ctypes.sizeof(addr))\n        ip_string = ctypes.create_string_buffer(128)\n        ip_string_size = ctypes.c_int(ctypes.sizeof(ip_string))\n\n        if address_family == socket.AF_INET:\n            if len(packed_ip) != ctypes.sizeof(addr.ipv4_addr):\n                raise socket.error('packed IP wrong length for inet_ntoa')\n            ctypes.memmove(addr.ipv4_addr, packed_ip, 4)\n        elif address_family == socket.AF_INET6:\n            if len(packed_ip) != ctypes.sizeof(addr.ipv6_addr):\n                raise socket.error('packed IP wrong length for inet_ntoa')\n            ctypes.memmove(addr.ipv6_addr, packed_ip, 16)\n        else:\n            raise socket.error('unknown address family')\n\n        if WSAAddressToStringA(\n                ctypes.byref(addr),\n                addr_size,\n                None,\n                ip_string,\n                ctypes.byref(ip_string_size)\n        ) != 0:\n            raise socket.error(ctypes.FormatError())\n\n        return ip_string[:ip_string_size.value - 1]\n\n\n    socket.inet_pton = _win_inet_pton\n    socket.inet_ntop = _win_inet_ntop\n\n\n# https://github.com/kennethreitz/requests/blob/master/requests/utils.py\ndef dotted_netmask(mask):\n    \"\"\"\n    Converts mask from /xx format to xxx.xxx.xxx.xxx\n    Example: if mask is 24 function returns 255.255.255.0\n    \"\"\"\n    mask = int(mask)\n    bits = 0xffffffff ^ (1 << 32 - mask) - 1\n    return socket.inet_ntoa(struct.pack('>I', bits))\n\n\n# http://en.wikipedia.org/wiki/Private_network\nprivate_ipv4s = [\n    ('10.0.0.0', 8),  # 10.0.0.0 - 10.255.255.255\n    ('172.16.0.0', 12),  # 172.16.0.0 - 172.31.255.255\n    ('192.168.0.0', 16),  # 192.168.0.0 - 192.168.255.255\n]\n\n\n# https://github.com/kennethreitz/requests/blob/master/requests/utils.py\ndef is_ipv4(ip):\n    \"\"\"\n    Returns True if the IPv4 address ia valid, otherwise returns False.\n    \"\"\"\n    try:\n        socket.inet_aton(ip)\n    except socket.error:\n        return False\n    return True\n\n\ndef is_ipv6(ip):\n    \"\"\"\n    Returns True if the IPv6 address ia valid, otherwise returns False.\n    \"\"\"\n    try:\n        socket.inet_pton(socket.AF_INET6, ip)\n    except socket.error:\n        return False\n    return True\n\n\ndef get_free_port():\n    with closing(socket.socket(socket.AF_INET, type=socket.SOCK_STREAM)) as s:\n        s.bind(('127.0.0.1', 0))\n        _, port = s.getsockname()\n    return port\n\n\n# https://stackoverflow.com/questions/5619685/conversion-from-ip-string-to-integer-and-backward-in-python\n# https://stackoverflow.com/questions/11894717/python-convert-ipv6-to-an-integer\ndef ip2int(ip_str):\n    \"\"\"\n    Convert ip to integer. Support IPV4 and IPV6.\n    Raise `ValueError` if convert failed.\n    \"\"\"\n    try:\n        return struct.unpack(\"!I\", socket.inet_aton(ip_str))[0]\n    except socket.error:\n        pass\n\n    try:\n        return hex2dec(binascii.hexlify(socket.inet_pton(socket.AF_INET6, ip_str)))\n    except socket.error:\n        pass\n\n    raise ValueError('{!r} does not appear to be an IPv4 or IPv6 address'.format(ip_str))\n\n\n# https://stackoverflow.com/questions/5619685/conversion-from-ip-string-to-integer-and-backward-in-python\ndef int2ip(ip_int):\n    \"\"\"\n    Convert integer to ip. Support IPV4 and IPV6.\n    Raise `ValueError` if convert failed.\n    \"\"\"\n    try:\n        return socket.inet_ntoa(struct.pack(\"!I\", ip_int))\n    except (socket.error, struct.error):\n        pass\n\n    try:\n        ip_str = socket.inet_ntop(socket.AF_INET6, binascii.unhexlify(dec2hex(ip_int)))\n        return safeunicode(ip_str, encoding='ascii')\n    except (socket.error, struct.error):\n        pass\n\n    raise ValueError('{!r} does not appear to be an IPv4 or IPv6 address'.format(ip_int))\n"
  },
  {
    "path": "pydu/path.py",
    "content": "import os\nfrom contextlib import contextmanager\n\n\n@contextmanager\ndef cd(path):\n    \"\"\"\n    Context manager for cd the given path.\n    \"\"\"\n    cwd = os.getcwd()\n    os.chdir(path)\n    yield\n    os.chdir(cwd)\n\n\ndef is_super_path(path1, path2):\n    \"\"\"\n    Whether `path1` is the super path of `path2`.\n    Note that if `path1` is same as `path2`, it's also regarded as\n    the super path os `path2`.\n    For instance \"/\", \"/opt\" and \"/opt/test\" are all the super paths of \"/opt/test\",\n    while \"/opt/t\" is the super path of \"/opt/test\".\n    \"\"\"\n    path1 = os.path.normpath(path1)\n    current_path2 = os.path.normpath(path2)\n    parent_path2 = os.path.dirname(current_path2)\n    if path1 == current_path2:\n        return True\n\n    while parent_path2 != current_path2:\n        if path1 == parent_path2:\n            return True\n        current_path2 = parent_path2\n        parent_path2 = os.path.dirname(parent_path2)\n\n    return False\n\n\ndef normjoin(path, *paths):\n    \"\"\"Join one or more path components intelligently and normalize it.\"\"\"\n    return os.path.normpath(os.path.join(path, *paths))\n\n\ndef filename(path):\n    \"\"\"Return the filename without extension.\"\"\"\n    return os.path.splitext(os.path.basename(path))[0]\n\n\ndef fileext(path):\n    \"\"\"\n    Return the file extension.\n    If file has not extension, return empty string.\n    \"\"\"\n    return os.path.splitext(os.path.basename(path))[1]\n"
  },
  {
    "path": "pydu/platform.py",
    "content": "import os\nimport sys\n\n\nWINDOWS = os.name == 'nt'\nLINUX = sys.platform.startswith('linux')\nPOSIX = os.name == 'posix'\nDARWIN = sys.platform.startswith('darwin')\nSUNOS = sys.platform.startswith('sunos')\nSMARTOS = os.uname()[3].startswith('joyent_') if SUNOS else False\nFREEBSD = sys.platform.startswith('freebsd')\nNETBSD = sys.platform.startswith('netbsd')\nOPENBSD = sys.platform.startswith('openbsd')\nAIX = sys.platform.startswith('aix')\n"
  },
  {
    "path": "pydu/process.py",
    "content": "try:\n    import psutil\nexcept ImportError:\n    raise ImportError('Need to pip install psutil if you use pydu.process')\n\nfrom .path import is_super_path\n\n\ndef get_processes_by_path(path):\n    \"\"\"\n    Get processes which are running on given path or sub path of given path.\n    \"\"\"\n    pinfos = []\n    for proc in psutil.process_iter():\n        pinfo = proc.as_dict(attrs=['pid', 'name', 'exe', 'cwd', 'open_files'])\n\n        using_paths = []\n        if pinfo['exe']:\n            using_paths.append(pinfo['exe'])\n        if pinfo['cwd']:\n            using_paths.append(pinfo['cwd'])\n        if pinfo['open_files']:\n            using_paths.extend(pinfo['open_files'])\n\n        for using_path in using_paths:\n            if is_super_path(path, using_path):\n                continue\n            pinfos.append({\n                'pid': pinfo['pid'],\n                'name': pinfo['name'],\n                'cmdline': pinfo['exe']\n            })\n    return pinfos\n"
  },
  {
    "path": "pydu/request.py",
    "content": "import os\nimport shutil\nimport tempfile\nimport socket\n\nfrom . import logger\nfrom .string import safeunicode\nfrom .compat import PY2, string_types, urlparse, urlib, urlencode\n\n\nclass FileName(object):\n    @staticmethod\n    def from_url(url):\n        \"\"\"\n        Detected filename as unicode or None\n        \"\"\"\n        filename = os.path.basename(urlparse.urlparse(url).path)\n        if len(filename.strip(' \\n\\t.')) == 0:\n            return None\n        return safeunicode(filename)\n\n    # http://greenbytes.de/tech/tc2231/\n    @staticmethod\n    def from_headers(headers):\n        \"\"\"\n        Detect filename from Content-Disposition headers if present.\n\n        headers: as dict, list or string\n        \"\"\"\n        if not headers:\n            return None\n\n        if isinstance(headers, string_types):\n            headers = [line.split(':', 1) for line in headers.splitlines()]\n        if isinstance(headers, list):\n            headers = dict(headers)\n\n        cdisp = headers.get(\"Content-Disposition\")\n        if not cdisp:\n            return None\n\n        cdtype = cdisp.split(';')\n        if len(cdtype) == 1:\n            return None\n        if cdtype[0].strip().lower() not in ('inline', 'attachment'):\n            return None\n\n        # several filename params is illegal, but just in case\n        fnames = [x for x in cdtype[1:] if x.strip().startswith('filename=')]\n        if len(fnames) > 1:\n            return None\n\n        name = fnames[0].split('=')[1].strip(' \\t\"')\n        name = os.path.basename(name)\n        if not name:\n            return None\n        return name\n\n    @classmethod\n    def from_any(cls, dst=None, headers=None, url=None):\n        return dst or cls.from_headers(headers) or cls.from_url(url)\n\n\n# http://bitbucket.org/techtonik/python-wget/\ndef download(url, dst=None):\n    \"\"\"\n    High level function, which downloads URL into tmp file in current\n    directory and then renames it to filename autodetected from either URL\n    or HTTP headers.\n\n    url: which url to download\n    dst: filename or directory of destination\n    \"\"\"\n    # detect of dst is a directory\n    dst_ = None\n    if dst and os.path.isdir(dst):\n        dst_ = dst\n        dst = None\n\n    # get filename for temp file in current directory\n    prefix = FileName.from_any(dst=dst, url=url)\n    fd, tmpfile = tempfile.mkstemp(\".tmp\", prefix=prefix, dir=\".\")\n    os.close(fd)\n    os.unlink(tmpfile)\n\n    if PY2:\n        binurl = url\n    else:\n        # Python 3 can not quote URL as needed\n        binurl = list(urlparse.urlsplit(url))\n        binurl[2] = urlparse.quote(binurl[2])\n        binurl = urlparse.urlunsplit(binurl)\n    tmpfile, headers = urlib.urlretrieve(binurl, tmpfile)\n    filename = FileName.from_any(dst=dst, headers=headers, url=url)\n    if dst_:\n        filename = os.path.join(dst_, filename)\n\n    if os.path.exists(filename):\n        os.unlink(filename)\n    shutil.move(tmpfile, filename)\n\n    return filename\n\n\ndef check_connect(ip, port, retry=1, timeout=0.5):\n    \"\"\"\n    Check whether given ``ip`` and ``port`` could connect or not.\n    It will ``retry`` and ``timeout`` on given.\n    \"\"\"\n    while retry:\n        try:\n            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n        except socket.error as e:\n            logger.exception(e)\n            retry -= 1\n            continue\n\n        try:\n            s.settimeout(timeout)\n            s.connect((ip, port))\n            return s.getsockname()[0]\n        except socket.error:\n            logger.error(\"Connect to ip:%s port:%d fail\", ip, port)\n            s.close()\n        finally:\n            retry -= 1\n    return None\n\n\ndef update_query_params(url, params):\n    \"\"\"\n    Update query params of given url and return new url.\n    \"\"\"\n    parts = list(urlparse.urlparse(url))\n    query = dict(urlparse.parse_qsl(parts[4]))\n    query.update(params)\n    parts[4] = urlencode(query)\n    new_url = urlparse.urlunparse(parts)\n    return new_url\n\n\ndef cookies_str_to_dict(cookies):\n    \"\"\"\n    Convert cookies from str to dict.\n    \"\"\"\n    if not isinstance(cookies, str):\n        raise TypeError('Invalid type of cookies_string !')\n\n    cookies_obj = {}\n    for item in cookies.split(';'):\n        item = item.strip().replace('\\t', '').replace('\\n', '')\n        if '=' not in item:\n            continue\n        key, value = item.split('=', 1)\n        cookies_obj[key] = value\n    return cookies_obj\n"
  },
  {
    "path": "pydu/set.py",
    "content": "# coding: utf-8\nimport collections\n\n\nclass OrderedSet(object):\n    \"\"\"\n    A set which keeps the ordering of the inserted items.\n    \"\"\"\n\n    def __init__(self, iterable=None):\n        self.dict = collections.OrderedDict.fromkeys(iterable or ())\n\n    def add(self, item):\n        self.dict[item] = None\n\n    def remove(self, item):\n        del self.dict[item]\n\n    def discard(self, item):\n        try:\n            self.remove(item)\n        except KeyError:\n            pass\n\n    def __iter__(self):\n        return iter(self.dict)\n\n    def __contains__(self, item):\n        return item in self.dict\n\n    def __bool__(self):\n        return bool(self.dict)\n\n    def __nonzero__(self):\n        return bool(self.dict)\n\n    def __len__(self):\n        return len(self.dict)\n"
  },
  {
    "path": "pydu/slot.py",
    "content": "from .compat import iteritems, izip\n\n\nclass SlotBase(object):\n    \"\"\"\n    Base class for class using __slots__.\n    If some args or kwargs are not given when initialize class,\n    the value of them will be set with ``None``.\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        setted = set()\n        kwargs_ = dict(izip(self.__slots__, args))\n        kwargs_.update(kwargs)\n        for key, value in iteritems(kwargs_):\n            setattr(self, key, value)\n            setted.add(key)\n        for key in set(self.__slots__) - setted:\n            setattr(self, key, None)\n"
  },
  {
    "path": "pydu/string.py",
    "content": "# coding: utf-8\nimport locale\nfrom .compat import text_type\n\n\npreferredencoding = locale.getpreferredencoding()\n\n\ndef safeunicode(obj, encoding='utf-8'):\n    \"\"\"\n    Converts any given object to unicode string.\n\n        >>> safeunicode('hello')\n        u'hello'\n        >>> safeunicode(2)\n        u'2'\n        >>> safeunicode('\\xe4\\xb8\\xad\\xe6\\x96\\x87')\n        u'中文'\n    \"\"\"\n    t = type(obj)\n    if t is text_type:\n        return obj\n    elif t is bytes:\n        return obj.decode(encoding)\n    else:\n        return text_type(obj)\n\n\ndef safeencode(obj, encoding='utf-8'):\n    \"\"\"\n    Converts any given object to encoded string (default: utf-8).\n\n        >>> safestr('hello')\n        'hello'\n        >>> safestr(2)\n        '2'\n    \"\"\"\n    t = type(obj)\n    if t is text_type:\n        return obj.encode(encoding)\n    elif t is bytes:\n        return obj\n    else:\n        return text_type(obj).encode(encoding)\n\n\niters = [list, tuple, set, frozenset]\nclass _hack(tuple): pass\niters = _hack(iters)\niters.__doc__ = \"\"\"\nA list of iterable items (like lists, but not strings). Includes whichever\nof lists, tuples, sets, and Sets are available in this version of Python.\n\"\"\"\n\n\ndef _strips(direction, text, remove):\n    if isinstance(remove, iters):\n        for subr in remove:\n            text = _strips(direction, text, subr)\n        return text\n\n    if direction == 'l':\n        if text.startswith(remove):\n            return text[len(remove):]\n    elif direction == 'r':\n        if text.endswith(remove):\n            return text[:-len(remove) or None]\n    else:\n        raise ValueError('Direction needs to be r or l.')\n    return text\n\n\ndef rstrips(text, remove):\n    \"\"\"\n    removes the string `remove` from the right of `text`\n        >>> rstrips('foobar', 'bar')\n        'foo'\n    \"\"\"\n    return _strips('r', text, remove)\n\n\ndef lstrips(text, remove):\n    \"\"\"\n    removes the string `remove` from the left of `text`\n\n        >>> lstrips('foobar', 'foo')\n        'bar'\n        >>> lstrips('FOOBARBAZ', ['FOO', 'BAR'])\n        'BAZ'\n        >>> lstrips('FOOBARBAZ', ['BAR', 'FOO'])\n        'BARBAZ'\n\n    \"\"\"\n    return _strips('l', text, remove)\n\n\ndef strips(text, remove):\n    \"\"\"\n    removes the string `remove` from the both sides of `text`\n        >>> strips('foobarfoo', 'foo')\n        'bar'\n    \"\"\"\n    return rstrips(lstrips(text, remove), remove)\n\n\ndef common_prefix(l):\n    \"\"\"\n    Return common prefix of the stings\n        >>> common_prefix(['abcd', 'abc1'])\n        'abc'\n    \"\"\"\n    commons = []\n    for i in range(min(len(s) for s in l)):\n        common = l[0][i]\n        for c in l[1:]:\n            if c[i] != common:\n                return ''.join(commons)\n        commons.append(common)\n    return ''.join(commons)\n\n\ndef common_suffix(l):\n    \"\"\"\n    Return common suffix of the stings\n        >>> common_suffix(['dabc', '1abc'])\n        'abc'\n    \"\"\"\n    commons = []\n    for i in range(min(len(s) for s in l)):\n        common = l[0][-i-1]\n        for c in l[1:]:\n            if c[-i-1] != common:\n                return ''.join(reversed(commons))\n        commons.append(common)\n    return ''.join(reversed(commons))\n\n\ndef sort(s, reverse=False):\n    \"\"\"\n    Sort given string by ascending order.\n    If reverse is True, sorting given string by descending order.\n    \"\"\"\n    return ''.join(sorted(s, reverse=reverse))\n"
  },
  {
    "path": "pydu/system.py",
    "content": "import os\nimport sys\nimport stat\nimport shutil\nimport locale\n\nfrom . import logger\nfrom .platform import WINDOWS\nfrom .compat import PY2, builtins\n\n\n_openfiles = set()\n_origin_open = builtins.open\nif PY2:\n    _origin_file = builtins.file\n\n    class _trackfile(builtins.file):\n        def __init__(self, *args):\n            self.path = args[0]\n            logger.debug('Opening \"%s\"', self.path)\n            super(_trackfile, self).__init__(*args)\n            _openfiles.add(self)\n\n        def close(self):\n            logger.debug('Closing \"%s\"', self.path)\n            super(_trackfile, self).close()\n            _openfiles.remove(self)\n\n\n    def _trackopen(*args):\n        return _trackfile(*args)\nelse:\n    def _trackopen(*args, **kwargs):\n        f = _origin_open(*args, **kwargs)\n        path = args[0]\n        logger.debug('Opening \"%s\"', path)\n        _openfiles.add(f)\n\n        origin_close = f.close\n\n        def close():\n            logger.debug('Closing \"%s\"', path)\n            origin_close()\n            _openfiles.remove(f)\n        f.close = close\n        return f\n\n\nclass FileTracker(object):\n    @staticmethod\n    def track():\n        builtins.open = _trackopen\n        if PY2:\n            builtins.file = _trackfile\n\n    @staticmethod\n    def untrack():\n        builtins.open = _origin_open\n        if PY2:\n            builtins.file = _origin_file\n\n    @staticmethod\n    def get_openfiles():\n        return _openfiles\n\n\ndef makedirs(path, mode=0o755, ignore_errors=False, exist_ok=False):\n    \"\"\"\n    Create a leaf directory and all intermediate ones.\n\n    Based on os.makedirs, but also supports ignore_errors which will\n    ignore all errors raised by os.makedirs.\n    \"\"\"\n    if exist_ok and os.path.exists(path):\n        return\n    try:\n        os.makedirs(path, mode)\n    except:\n        if not ignore_errors:\n            raise OSError('Create dir: {!r} error.'.format(path))\n\n\ndef remove(path, ignore_errors=False, onerror=None):\n    \"\"\"\n    Remove a file or directory.\n\n    If ignore_errors is set, errors are ignored; otherwise, if onerror\n    is set, it is called to handle the error with arguments (func,\n    path, exc_info) where func is platform and implementation dependent;\n    path is the argument to that function that caused it to fail; and\n    exc_info is a tuple returned by sys.exc_info().  If ignore_errors\n    is False and onerror is None, it attempts to set path as writeable and\n    then proceed with deletion if path is read-only, or raise an exception\n    if path is not read-only.\n    \"\"\"\n    if ignore_errors:\n        def onerror(func, path, exc):\n            pass\n    elif onerror is None:\n        def onerror(func, path, exc):\n            try:\n                if (os.stat(path).st_mode & stat.S_IREAD) or not os.access(path, os.W_OK):\n                    os.chmod(path, stat.S_IWRITE | stat.S_IWUSR)\n                    func(path)\n                else:\n                    exc_type, exc_exception, exc_tb = exc\n                    raise exc_exception\n            except Exception as e:\n                raise OSError('Remove path: {!r} error. Reason: {}'.format(path, e))\n\n    if os.path.isdir(path):\n        shutil.rmtree(path, ignore_errors=ignore_errors, onerror=onerror)\n    else:\n        try:\n            os.remove(path)\n        except:\n            onerror(os.remove, path, sys.exc_info())\n\n\ndef removes(paths, ignore_errors=False, onerror=None):\n    \"\"\"\n    Remove a list of file and/or directory.\n\n    If ignore_errors is set, errors are ignored; otherwise, if onerror\n    is set, it is called to handle the error with arguments (func,\n    path, exc_info) where func is platform and implementation dependent;\n    path is the argument to that function that caused it to fail; and\n    exc_info is a tuple returned by sys.exc_info().  If ignore_errors\n    is False and onerror is None, an exception is raised.\n    \"\"\"\n    for path in paths:\n        remove(path, ignore_errors=ignore_errors, onerror=onerror)\n\n\ndef open_file(path, mode='wb+', buffer_size=-1, ignore_errors=False):\n    \"\"\"\n    Open a file, defualt mode 'wb+'.\n\n    If path not exists, it will be created automatically.\n    If ignore_errors is set, errors are ignored.\n    \"\"\"\n    f = None\n    try:\n        if path and not os.path.isdir(path):\n            makedirs(os.path.dirname(path), exist_ok=True)\n            f = open(path, mode, buffer_size)\n    except:\n        if not ignore_errors:\n            raise OSError('Open file: {!r} error'.format(path))\n    return f\n\n\ndef copy(src, dst, ignore_errors=False, follow_symlinks=True):\n    \"\"\"\n    Copy data and mode bits (\"cp src dst\").\n\n    Both the source and destination may be a directory.\n\n    When copy a directory,which contains a symlink, If the optional\n    symlinks flag is true, symbolic links in the source tree result\n    in symbolic links in the destination tree; if it is false, the\n    contents of the files pointed to by symbolic links are copied.\n    If the file pointed by the symlink doesn't exist, an exception\n    will be raise.\n\n    When copy a file,if follow_symlinks is false and src is a symbolic\n    link, a new symlink will be created instead of copying the file it\n    points to,else the contents of the file pointed to by symbolic links\n    is copied.\n\n    If source and destination are the same file, a SameFileError will be\n    raised.\n\n    If ignore_errors is set, errors are ignored.\n    \"\"\"\n    try:\n        if os.path.isdir(src):\n            shutil.copytree(src, dst, symlinks=follow_symlinks)\n        else:\n            if not follow_symlinks and os.path.islink(src):\n                os.symlink(os.readlink(src), dst)\n            else:\n                shutil.copy(src, dst)\n    except:\n        if not ignore_errors:\n            raise OSError('Copy {!r} to {!r} error'.format(src, dst))\n\n\ndef touch(path):\n    \"\"\"\n    Open a file as write,and then close it.\n    \"\"\"\n    with open(path, 'w'):\n        pass\n\n\ndef chmod(path, mode, recursive=False):\n    \"\"\"\n    Change permissions to the given mode.\n    If `recursive` is True perform recursively.\n\n        >>> chmod('/opt/sometest', 0o755)\n        >>> oct(os.stat('/opt/sometest').st_mode)[-3:]\n        755\n    \"\"\"\n    chmod_ = os.chmod\n    if recursive and os.path.isdir(path):\n        for dirpath, _, filenames in os.walk(path):\n            chmod_(dirpath, mode)\n            for filename in filenames:\n                chmod_(os.path.join(dirpath, filename), mode)\n    else:\n        os.chmod(path, mode)\n\n\nif PY2:\n    # shutil.which from Python3\n    def which(cmd, mode=os.F_OK | os.X_OK, path=None):\n        \"\"\"\n        Given a command, mode, and a PATH string, return the path which\n        conforms to the given mode on the PATH, or None if there is no such\n        file.\n\n        `mode` defaults to os.F_OK | os.X_OK. `path` defaults to the result\n        of os.environ.get(\"PATH\"), or can be overridden with a custom search\n        path.\n        \"\"\"\n        # Check that a given file can be accessed with the correct mode.\n        # Additionally check that `file` is not a directory, as on Windows\n        # directories pass the os.access check.\n        def _access_check(fn, mode):\n            return (os.path.exists(fn) and os.access(fn, mode)\n                    and not os.path.isdir(fn))\n\n        # If we're given a path with a directory part, look it up directly rather\n        # than referring to PATH directories. This includes checking relative to the\n        # current directory, e.g. ./script\n        if os.path.dirname(cmd):\n            if _access_check(cmd, mode):\n                return cmd\n            return None\n\n        if path is None:\n            path = os.environ.get(\"PATH\", os.defpath)\n        if not path:\n            return None\n        path = path.split(os.pathsep)\n\n        if WINDOWS:\n            # The current directory takes precedence on Windows.\n            if not os.curdir in path:\n                path.insert(0, os.curdir)\n\n            # PATHEXT is necessary to check on Windows.\n            pathext = os.environ.get(\"PATHEXT\", \"\").split(os.pathsep)\n            # See if the given file matches any of the expected path extensions.\n            # This will allow us to short circuit when given \"python.exe\".\n            # If it does match, only test that one, otherwise we have to try\n            # others.\n            if any(cmd.lower().endswith(ext.lower()) for ext in pathext):\n                files = [cmd]\n            else:\n                files = [cmd + ext for ext in pathext]\n        else:\n            # On other platforms you don't have things like PATHEXT to tell you\n            # what file suffixes are executable, so just pass on cmd as-is.\n            files = [cmd]\n\n        seen = set()\n        for dir in path:\n            normdir = os.path.normcase(dir)\n            if not normdir in seen:\n                seen.add(normdir)\n                for thefile in files:\n                    name = os.path.join(dir, thefile)\n                    if _access_check(name, mode):\n                        return name\n        return None\nelse:\n    which = shutil.which\n\n\nif WINDOWS:\n    # For Windows system\n    from ctypes import windll\n\n    class chcp(object):\n        \"\"\"\n        Context manager which sets the active code page number.\n        It could also be used as function.\n        \"\"\"\n        def __init__(self, code):\n            self.origin_code = windll.kernel32.GetConsoleOutputCP()\n            self.code = code\n            windll.kernel32.SetConsoleOutputCP(code)\n\n        def __enter__(self):\n            return self\n\n        def __exit__(self, exc_type, exc_val, exc_tb):\n            windll.kernel32.SetConsoleOutputCP(self.origin_code)\n\n        def __repr__(self):\n            return '<active code page number: {}>'.format(self.code)\nelse:\n    # For non Windows system\n    def symlink(src, dst, overwrite=False, ignore_errors=False):\n        \"\"\"\n        Create a symbolic link pointing to source named link_name.\n\n        If dist is exist and overwrite is true,a new symlink will be created\n\n        If ignore_errors is set, errors are ignored.\n        \"\"\"\n        try:\n            if os.path.exists(dst):\n                if overwrite:\n                    remove(dst)\n                else:\n                    return\n            os.symlink(src, dst)\n        except Exception:\n            if not ignore_errors:\n                raise OSError('Link {!r} to {!r} error'.format(dst, src))\n\n\n    def link(src, dst, overwrite=False, ignore_errors=False):\n        \"\"\"\n        Create a hard link pointing to source named link_name.\n\n        If dist is exist and overwrite is true,a new symlink will be created\n\n        If ignore_errors is set, errors are ignored.\n        \"\"\"\n        try:\n            if os.path.exists(dst):\n                if overwrite:\n                    remove(dst)\n                else:\n                    return\n            os.link(src, dst)\n        except:\n            if not ignore_errors:\n                raise OSError('Link {!r} to {!r} error'.format(dst, src))\n\n\ndef preferredencoding():\n    \"\"\"\n    Get best encoding for the system.\n    \"\"\"\n    try:\n        encoding = locale.getpreferredencoding()\n        'test encoding'.encode(encoding)\n    except UnicodeEncodeError:\n        encoding = 'UTF-8'\n\n    return encoding\n"
  },
  {
    "path": "pydu/unit.py",
    "content": "\n\nBYTE_UNITS = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')\n\n\nclass Bytes(object):\n    \"\"\"\n    Supply several methods dealing with bytes.\n    \"\"\"\n    def __init__(self, bytes):\n        self.bytes = bytes\n\n    def convert(self, unit=None, multiple=1024):\n        \"\"\"\n        Convert bytes with given ``unit``.\n        If `unit` is None, convert bytes with suitable unit.\n        Convert `multiple` is default to be 1024.\n        \"\"\"\n        step = 0\n        if not unit:\n            while self.bytes >= multiple and step < len(BYTE_UNITS) - 1:\n                self.bytes /= multiple\n                step += 1\n            unit = BYTE_UNITS[step]\n\n        else:  # convert to specific unit\n            index_of_unit = BYTE_UNITS.index(unit)\n            while len(BYTE_UNITS) - 1 > step and index_of_unit != step:\n                self.bytes /= multiple\n                step += 1\n        return self.bytes, unit\n"
  },
  {
    "path": "requirements-dev.txt",
    "content": "pytest>=2.8.0\npytest-xdist\ncoverage\npsutil\n"
  },
  {
    "path": "setup.cfg",
    "content": "[bdist_wheel]\nuniversal = 1\n\n[metadata]\nlicense_file = LICENSE.txt\n"
  },
  {
    "path": "setup.py",
    "content": "import sys\nfrom pydu import __version__\nfrom pydu.compat import PY2\nfrom setuptools import setup, find_packages\nfrom setuptools.command.test import test as TestCommand\n\n\nclass PyTest(TestCommand):\n    user_options = [('pytest-args=', 'a', \"Arguments to pass into py.test\")]\n\n    def initialize_options(self):\n        TestCommand.initialize_options(self)\n        try:\n            from multiprocessing import cpu_count\n            self.pytest_args = ['-n', str(cpu_count())]\n        except (ImportError, NotImplementedError):\n            self.pytest_args = ['-n', '1']\n\n    def finalize_options(self):\n        TestCommand.finalize_options(self)\n        self.test_args = []\n        self.test_suite = True\n\n    def run_tests(self):\n        import pytest\n\n        errno = pytest.main(self.pytest_args)\n        sys.exit(errno)\n\n\ntest_requirements = []\nfor line in open('requirements-dev.txt'):\n    requirement = line.strip()\n    if requirement:\n        test_requirements.append(requirement)\n\n\nopen_kwargs = {} if PY2 else {'encoding': 'utf-8'}\nsetup(\n    name=\"pydu\",\n    version=__version__,\n    description=\"Useful data structures, utils for Python.\",\n    long_description=open('README.md', **open_kwargs).read(),\n    long_description_content_type='text/markdown',\n    author=\"Prodesire\",\n    author_email='wangbinxin001@126.com',\n    license='MIT License',\n    url=\"https://github.com/Prodesire/pydu\",\n    cmdclass={'test': PyTest},\n    tests_require=test_requirements,\n    packages=find_packages(),\n    classifiers=[\n        'Operating System :: OS Independent',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: MIT License',\n        'Programming Language :: Python',\n        'Programming Language :: Python :: Implementation',\n        'Programming Language :: Python :: 2',\n        'Programming Language :: Python :: 2.7',\n        'Programming Language :: Python :: 3',\n        'Programming Language :: Python :: 3.5',\n        'Programming Language :: Python :: 3.6',\n        'Programming Language :: Python :: 3.7',\n        'Programming Language :: Python :: 3.8',\n        'Topic :: Software Development :: Libraries'\n    ],\n)\n"
  },
  {
    "path": "stubs/pydu/__init__.pyi",
    "content": ""
  },
  {
    "path": "stubs/pydu/archive.pyi",
    "content": "from tarfile import TarFile\nfrom zipfile import ZipFile\nfrom typing import List\n\n\nclass Archive(object):\n    _archive = ...  # type: BaseArchive\n    def __init__(self, file: file, ext: str='') -> None: ...\n    def _archive_cls(self, file: file, ext: str='') -> None: ...\n    def extract(self, dst: str='') -> None: ...\n    def list(self) -> None: ...\n    def filenames(self) -> list: ...\n    def close(self) -> None: ...\n\nclass BaseArchive(object):\n    @staticmethod\n    def _copy_permissions(mode: int, filename: str) -> None: ...\n    def split_leading_dir(self, path: str) -> None: ...\n    def has_leading_dir(self, paths: List[str]) -> None: ...\n    def extract(self, dst: str) -> None: ...\n    def list(self) -> list: ...\n    def filenames(self) -> List[str]: ...\n\nclass TarArchive(BaseArchive):\n    _archive = ...  # type: TarFile\n    def extract(self, dst: str) -> None: ...\n    def list(self) -> None: ...\n    def filenames(self) -> List[str]: ...\n    def close(self) -> None: ...\n\nclass ZipArchive(BaseArchive):\n    _archive = ...  # type: ZipFile\n    def extract(self, dst: str) -> None: ...\n    def list(self) -> None: ...\n    def filenames(self) -> List[str]: ...\n    def close(self) -> None: ...\n"
  },
  {
    "path": "stubs/pydu/cmd.pyi",
    "content": "\"\"\"Stubs for cmd\"\"\"\nfrom typing import Tuple, List, Union\n\n\nclass TimeoutExpired(Exception):\n\n    def __init__(self, cmd: str,\n                 timeout: Union[int, float],\n                 output: dict=None,\n                 stderr: dict=None) -> None: ...\n\ndef run(cmd: str,\n        shell: bool=...,\n        env: dict=None,\n        timeout: Union[int, float]=...,\n        timeinterval: Union[int, float]=...) -> Tuple[int, str]: ...\ndef run_with_en_env(cmd: str,\n                    shell: bool=...,\n                    env: dict=None,\n                    timeout: Union[int, float]=...,\n                    timeinterval: Union[int, float]=...) -> Tuple[int, str]: ...\ndef terminate(pid: int) -> None: ...\ndef cmdline_argv() -> List: ...\n"
  },
  {
    "path": "stubs/pydu/console.pyi",
    "content": "\"\"\"Stubs for console\"\"\"\nfrom typing import Tuple\n\n\ndef console_size(fallback: Tuple[int, int]=...) -> Tuple[int, int]: ..."
  },
  {
    "path": "stubs/pydu/convert.pyi",
    "content": "from typing import Any\n\n\ndef boolean(obj: Any) -> bool: ...\ndef bin2oct(x: str) -> str: ...\ndef bin2dec(x: str) -> int: ...\ndef bin2hex(x: str) -> str: ...\ndef oct2bin(x: str) -> str: ...\ndef oct2dec(x: str) -> int: ...\ndef oct2hex(x: str) -> str: ...\ndef dec2bin(x: int) -> str: ...\ndef dec2oct(x: int) -> str: ...\ndef dec2hex(x: int) -> str: ...\ndef hex2bin(x: str) -> str: ...\ndef hex2oct(x: str) -> str: ...\ndef hex2dec(x: str) -> int: ...\n"
  },
  {
    "path": "stubs/pydu/dict.pyi",
    "content": "import collections\nfrom typing import Iterable, Tuple, Any\n\n\nclass CaseInsensitiveDict(collections.MutableMapping):\n    _store = ... # type: dict\n    def __init__(self, data: dict=None, **kwargs) -> None: ...\n    def lower_items(self) -> Iterable[Tuple[str, Any]]: ...\n"
  },
  {
    "path": "stubs/pydu/dt.pyi",
    "content": "from typing import Callable\n\n\nclass timer(object):\n    elapsed = ... # type: float\n    print_func = ... # type: Callable\n"
  },
  {
    "path": "stubs/pydu/environ.pyi",
    "content": "from typing import Dict, ContextManager, Union\n\n\nStrList = Union[str, list]\n\ndef environ(kwargs: Dict[str, str]) -> ContextManager[None]: ...\ndef path(append: StrList, prepend: StrList, replace: StrList) -> ContextManager[None]: ..."
  },
  {
    "path": "stubs/pydu/exception.pyi",
    "content": "from typing import ContextManager, Type, List, Any, Callable\nfrom pydu.compat import PY2\n\n\nif PY2:\n    def ignore(*exceptions: Type[BaseException]) -> ContextManager[None]: ...\nelse:\n    class ignore(ContextManager[None]):\n        def __init__(self, *exceptions: Type[BaseException]) -> None: ...\n\n\ndef default_if_except(exceptions_clses: List[Exception], default: Any=None) -> Callable: ...\n"
  },
  {
    "path": "stubs/pydu/functional.pyi",
    "content": "from typing import List, Callable, Any\n\n\ndef compose(*funcs: List[Callable]):\n    return Any\n"
  },
  {
    "path": "stubs/pydu/iter.pyi",
    "content": "from typing import Iterable, TypeVar, Optional, Callable\n\n\nT = TypeVar('T')\n\ndef first(iterable: Iterable[T]) -> T: ...\ndef last(iterable: Iterable[T]) -> Optional[T]: ...\ndef all(iterable: Iterable[T], predicate: Callable[[T], bool]) -> bool: ...\ndef any(iterable: Iterable[T], predicate: Callable[[T], bool]) -> bool: ...\ndef join(iterable: Iterable[T], separator: str) -> str: ...\n"
  },
  {
    "path": "stubs/pydu/list.pyi",
    "content": "from typing import Callable, Any, Hashable, Iterable\n\n\nKeyFunc = Callable[[Any], Hashable]\ndef uniq(seq: Iterable[Any], key: KeyFunc=None) -> list: ...\ndef tolist(obj: Any) -> list: ...\ndef flatten(seq: Iterable[Any]) -> Iterable[Any]: ...\n"
  },
  {
    "path": "stubs/pydu/misc.pyi",
    "content": "from typing import Callable, Any\n\n\nAnyCallable = Callable[..., Any]\n\ndef timeout(seconds: int, error_message: str) -> Callable[[AnyCallable], AnyCallable]: ...\ndef trace(func: AnyCallable) -> AnyCallable: ...\ndef memoize(func: AnyCallable) -> AnyCallable: ...\ndef memoize_when_activated(func: AnyCallable) -> AnyCallable: ...\ndef super_len(obj: Any) -> int: ..."
  },
  {
    "path": "stubs/pydu/network.pyi",
    "content": "from typing import Union\n\ndef dotted_netmask(mask: Union[int, str]) -> str: ...\ndef is_ipv4(ip: str) -> bool: ...\ndef is_ipv6(ip: str) -> bool: ...\ndef ip2int(ip_str) -> int: ...\ndef int2ip(ip_int: int) -> int: ...\n"
  },
  {
    "path": "stubs/pydu/path.pyi",
    "content": "from typing import ContextManager, List\n\n\ndef cd(path: str) -> ContextManager[None]: ...\ndef is_super_path(path1: str, path2: str) -> bool: ...\ndef normjoin(path: str, *paths: List(str)) -> str: ...\ndef filename(path: str) -> str: ...\ndef fileexe(path: str) -> str: ...\n"
  },
  {
    "path": "stubs/pydu/process.pyi",
    "content": "from typing import List, Dict, Union\n\ndef get_processes_by_path(path: str) -> List[Dict[str, Union[int, str]]]: ...\n"
  },
  {
    "path": "stubs/pydu/request.pyi",
    "content": "from typing import Union, Optional\n\n\nHeaders = Union[dict, list, str]\n\n\nclass FileName(object):\n    def from_url(self, url: str) -> str: None\n    def from_headers(self, headers: Headers) -> Optional[str]: ...\n    def from_any(cls, dst: str=None, headers: Headers=None, url: str=None) -> Optional[str]: ...\n\ndef download(url: str, dst: str=None) -> str: ...\ndef check_connect(ip: str, port: int, retry: int=1, timout: float=0.5) -> Optional[str]: ...\ndef update_query_params(url: str, params: dict) -> str: ...\ndef cookies_str_to_dict(cookies: str) -> dict: ...\n"
  },
  {
    "path": "stubs/pydu/set.pyi",
    "content": "from typing import Iterable, Tuple, Any\n\n\nclass OrderedSet(object):\n    def __init__(self, iterable: Iterable[Tuple[Any, Any]]=None) -> None: ...\n"
  },
  {
    "path": "stubs/pydu/string.pyi",
    "content": "from typing import Any\nfrom pydu.compat import text_type\n\n\ndef safeunicode(obj: Any, encoding: str) -> text_type: ...\ndef safeencode(obj: Any, encoding: str) -> bytes: ...\ndef _strips(direction: str, text: str, remove: str) -> str: ...\ndef rstrips(text: str, remove: str) -> str: ...\ndef lstrips(text: str, remove: str) -> str: ...\ndef strips(text: str, remove: str) -> str: ...\ndef common_prefix(l: list) -> str: ...\ndef common_suffix(l: list) -> str: ...\ndef sort(s: str, reverse: bool) -> str: ...\n"
  },
  {
    "path": "stubs/pydu/system.pyi",
    "content": "import os\nfrom typing import Callable, List\nfrom pydu.platform import WINDOWS\n\n\ndef makedirs(path: str, mode: int=0o755, ignore_errors: bool=False, exist_ok: bool=False) -> None: ...\ndef remove(path: str, ignore_errors: bool=False, onerror: Callable=None) -> None: ...\ndef removes(paths: List[str], ignore_errors: bool=False, onerror: Callable=None) -> None: ...\ndef open_file(path: str, mode: str='wb+', buffer_size: int=-1, ignore_errors: bool=False) -> None: ...\ndef copy(src: str, dst: str, ignore_errors: bool=False, follow_symlinks: bool=True) -> None: ...\ndef touch(path: str) -> None: ...\ndef chmod(path: str, mode: int, recursive: bool=False) -> None: ...\ndef which(cmd: str, mode: int=os.F_OK | os.X_OK, path: str=None) -> None: ...\nif WINDOWS:\n    class chcp(object):\n        def __init__(self, code: str) -> None: ...\nelse:\n    def symlink(src: str, dst: str, overwrite: bool=False, ignore_errors: bool=False) -> None: ...\n    def link(src: str, dst: str, overwrite: bool=False, ignore_errors: bool=False) -> None: ...\n"
  },
  {
    "path": "stubs/pydu/unit.pyi",
    "content": "from typing import Tuple\n\n\nclass Bytes(object):\n    bytes=... # type: str\n    def __init__(self, bytes: str) -> None: ...\n    def convert(self, unit: str=None, multiple: int=1024) -> Tuple[str, str]: ...\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/files/bad/unrecognized.txt",
    "content": "File with unrecognized archive extension.\n"
  },
  {
    "path": "tests/test_archive.py",
    "content": "# coding: utf-8\nimport os\nimport shutil\nimport tempfile\nimport unittest\nfrom os.path import isfile, join as pathjoin\n\nfrom pydu.archive import extract, UnrecognizedArchiveFormat\n\n\nTEST_DIR = os.path.dirname(os.path.realpath(__file__))\n\n\nclass TempDirMixin(object):\n    \"\"\"\n    Mixin class for TestCase subclasses to set up and tear down a temporary\n    directory for unpacking archives during tests.\n    \"\"\"\n\n    def setUp(self):\n        \"\"\"\n        Create temporary directory for testing extraction.\n        \"\"\"\n        self.tmpdir = tempfile.mkdtemp()\n        os.chdir(TEST_DIR)\n\n    def tearDown(self):\n        \"\"\"\n        Clean up temporary directory.\n        \"\"\"\n        shutil.rmtree(self.tmpdir)\n\n    def check_files(self, tmpdir):\n        self.assertTrue(isfile(pathjoin(tmpdir, '1')))\n        self.assertTrue(isfile(pathjoin(tmpdir, '2')))\n        self.assertTrue(isfile(pathjoin(tmpdir, 'foo', '1')))\n        self.assertTrue(isfile(pathjoin(tmpdir, 'foo', '2')))\n        self.assertTrue(isfile(pathjoin(tmpdir, 'foo', 'bar', '1')))\n        self.assertTrue(isfile(pathjoin(tmpdir, 'foo', 'bar', '2')))\n\n\nclass ArchiveTester(TempDirMixin):\n    \"\"\"\n    A mixin class to be used for testing many Archive methods for a single\n    archive file.\n    \"\"\"\n\n    archive = None\n    ext = ''\n\n    def setUp(self):\n        super(ArchiveTester, self).setUp()\n        self.archive_path = pathjoin(TEST_DIR, 'files', self.archive)\n\n    def test_extract(self):\n        extract(self.archive_path, self.tmpdir, ext=self.ext)\n        self.check_files(self.tmpdir)\n\n    def test_extract_fileobject(self):\n        with open(self.archive_path, 'rb') as f:\n            extract(f, self.tmpdir, ext=self.ext)\n            self.check_files(self.tmpdir)\n\n    def test_extract_no_to_path(self):\n        cur_dir = os.getcwd()\n        os.chdir(self.tmpdir)\n        extract(self.archive_path, ext=self.ext)\n        self.check_files(self.tmpdir)\n        os.chdir(cur_dir)\n\n    def test_extract_bad_fileobject(self):\n        class File:\n            pass\n        f = File()\n        self.assertRaises(UnrecognizedArchiveFormat, extract,\n                          (f, self.tmpdir), {'ext': self.ext})\n\n\nclass TestZip(ArchiveTester, unittest.TestCase):\n    archive = 'foobar.zip'\n\n\nclass TestTar(ArchiveTester, unittest.TestCase):\n    archive = 'foobar.tar'\n\n\nclass TestGzipTar(ArchiveTester, unittest.TestCase):\n    archive = 'foobar.tar.gz'\n\n\nclass TestBzip2Tar(ArchiveTester, unittest.TestCase):\n    archive = 'foobar.tar.bz2'\n\n\nclass TestNonAsciiNamedTar(ArchiveTester, unittest.TestCase):\n    archive = u'压缩.tgz'\n\n\nclass TestUnicodeNamedZip(ArchiveTester, unittest.TestCase):\n    archive = u'压缩.zip'\n\n\nclass TestExplicitExt(ArchiveTester, unittest.TestCase):\n    archive = 'foobar_tar_gz'\n    ext = '.tar.gz'\n"
  },
  {
    "path": "tests/test_cmd.py",
    "content": "import sys\nimport pytest\nimport time\nimport subprocess\nfrom pydu.compat import string_types\nfrom pydu.string import safeunicode\nfrom pydu.cmd import TimeoutExpired, run, run_with_en_env, terminate, cmdline_argv\n\n\ndef test_run():\n    retcode, output = run('echo hello', shell=True)\n    assert retcode == 0\n    assert safeunicode(output).rstrip('\\r\\n') == 'hello'\n\n    with pytest.raises(TimeoutExpired) as e:\n        cmd = '{} -c \"import time; time.sleep(1)\"'.format(sys.executable)\n        timeout = 0.2\n        run(cmd, shell=True, timeout=timeout, timeinterval=0.05)\n        assert e.cmd == cmd\n        assert e.timeout == timeout\n        assert hasattr(e, 'output')\n        assert hasattr(e, 'stderr')\n\n\ndef test_run_with_en_env():\n    _, output = run_with_en_env('nocmd', shell=True)\n    assert output.decode('ascii')\n\n    _, output = run_with_en_env(['nocmd'], shell=True)\n    assert output.decode('ascii')\n\n\ndef test_terminate():\n    p = subprocess.Popen('{} -c \"import time; time.sleep(1)\"'.format(sys.executable),\n                         shell=True)\n    terminate(p.pid)\n    time.sleep(0.1)\n    assert p.poll() is not None\n\n\ndef test_cmdline_argv():\n    argv = cmdline_argv()\n    for s in argv[1:]:\n        assert isinstance(s, string_types)\n"
  },
  {
    "path": "tests/test_compat.py",
    "content": "from pydu.compat import (PY2, iterkeys, itervalues, iteritems,\n                         text_type, string_types, numeric_types,\n                         is_iterable, has_next_attr, imap, cmp)\n\n\ndef test_itersth():\n    d = dict(a=1, b=2)\n    for key in iterkeys(d):\n        assert key in ('a', 'b')\n\n    for value in itervalues(d):\n        assert value in (1, 2)\n\n    for items in iteritems(d):\n        assert items in (('a', 1), ('b', 2))\n\n\ndef test_has_next_attr():\n    if PY2:\n        class NextAttr:\n            def next(self):\n                pass\n    else:\n        class NextAttr:\n            def __next__(self):\n                pass\n    assert has_next_attr(NextAttr())\n    assert not has_next_attr('')\n\n\ndef test_is_iterable():\n    assert is_iterable(list())\n    assert is_iterable(tuple())\n    assert is_iterable(dict())\n    assert is_iterable(set())\n    assert not is_iterable(1)\n\n\ndef test_types():\n    assert isinstance(u'a', text_type)\n\n    assert isinstance(u'a', string_types)\n    assert isinstance('a', string_types)\n\n    assert isinstance(1, numeric_types)\n    assert isinstance(2**50, numeric_types)\n\n\ndef test_urlmisc():\n    from pydu.compat import urljoin, urlib, urlparse\n\n\ndef test_imap():\n    assert list(imap(pow, (2, 3, 10), (5, 2, 3))) == [32, 9, 1000]\n    assert list(imap(max, (1, 4, 7), (2, 3, 8))) == [2, 4, 8]\n\n\ndef test_cmp():\n    assert cmp(1, 2) < 0\n    assert cmp(1, 1) == 0\n    assert cmp(2, 1) > 0\n"
  },
  {
    "path": "tests/test_console.py",
    "content": "from pydu.console import console_size\n\n\ndef test_console_size():\n    size = console_size()\n    assert isinstance(size, tuple)\n    assert len(size) == 2\n"
  },
  {
    "path": "tests/test_convert.py",
    "content": "import pytest\nfrom pydu.convert import (boolean,\n                          bin2oct, bin2dec, bin2hex,\n                          oct2bin, oct2dec, oct2hex,\n                          dec2bin, dec2oct, dec2hex,\n                          hex2bin, hex2oct, hex2dec)\n\n\nBIG_NUM_STR = '10'*50\nBIG_NUM = 10**50\n\n\nclass TestBoolean:\n    def test_accepted_text(self):\n        for text in ('yes', 'y', 'on', 'true', 't', '1'):\n            assert boolean(text)\n            assert boolean(text.upper())\n\n        for text in ('no', 'n', 'off', 'false', 'f', '0'):\n            assert not boolean(text)\n            assert not boolean(text.upper())\n\n    @pytest.mark.parametrize('text', ('a', 'b'))\n    def test_unaccepted_text(self, text):\n        with pytest.raises(ValueError):\n            boolean(text)\n\n    def test_nonstring(self):\n        for obj in (10, [1], {1: 1}):\n            assert boolean(obj)\n\n        for obj in (0, [], {}):\n            assert not boolean(obj)\n\n\ndef test_bin2oct():\n    assert bin2oct('1001') == '11'\n    assert 'L' not in bin2oct(BIG_NUM_STR)\n\n\ndef test_bin2dec():\n    assert bin2dec('11') == 3\n\n\ndef test_bin2hex():\n    assert bin2hex('11010') == '1a'\n    assert 'L' not in bin2hex(BIG_NUM_STR)\n\n\ndef test_oct2bin():\n    assert oct2bin('11') == '1001'\n    assert 'L' not in oct2bin(BIG_NUM_STR)\n\n\ndef test_oct2dec():\n    assert oct2dec('11') == 9\n\n\ndef test_oct2hex():\n    assert oct2hex('32') == '1a'\n    assert 'L' not in oct2hex(BIG_NUM_STR)\n\n\ndef test_dec2bin():\n    assert dec2bin(3) == '11'\n    assert 'L' not in dec2bin(BIG_NUM)\n\n\ndef test_dec2oct():\n    assert dec2oct(9) == '11'\n    assert 'L' not in dec2oct(BIG_NUM)\n\n\ndef test_dec2hex():\n    assert dec2hex(26) == '1a'\n    assert 'L' not in dec2hex(BIG_NUM)\n\n\ndef test_hex2bin():\n    assert hex2bin('1a') == '11010'\n    assert 'L' not in hex2bin(BIG_NUM_STR)\n\n\ndef test_hex2oct():\n    assert hex2oct('1a') == '32'\n    assert 'L' not in hex2oct(BIG_NUM_STR)\n\n\ndef test_hex2dec():\n    assert hex2dec('1a') == 26\n"
  },
  {
    "path": "tests/test_dict.py",
    "content": "import pytest\nimport unittest\n\nfrom pydu.dict import AttrDict, LookupDict, CaseInsensitiveDict, OrderedDefaultDict, attrify\n\n\nclass TestAttrDict:\n\n    def test_attr_access_with_init(self):\n        d = AttrDict(key=1)\n        assert d['key'] == 1\n        assert d.key == 1\n\n    def test_attr_access_without_init(self):\n        d = AttrDict()\n        d['key'] = 1\n        assert d['key'] == 1\n        assert d.key == 1\n\n        d.anotherkey = 1\n        assert d.anotherkey == 1\n        assert d['anotherkey'] == 1\n\n    def test_attr_delete(self):\n        d = AttrDict(key=1)\n        del d.key\n        with pytest.raises(AttributeError):\n            del d.key\n\n    def test_repr(self):\n        d = AttrDict()\n        assert repr(d) == '<AttrDict {}>'\n\n\nclass TestLooUpDict:\n\n    def test_key_exist(self):\n        d = LookupDict()\n        d['key'] = 1\n        assert d['key'] == 1\n\n    def test_key_not_exist(self):\n        d = LookupDict()\n        assert d['key'] is None\n\n\nclass TestCaseInsensitiveDict(unittest.TestCase):\n    def setUp(self):\n        self.d = CaseInsensitiveDict()\n        self.d['Accept'] = 1\n\n    def test_ci_dict_set(self):\n        assert self.d['aCCept'] == 1\n        assert list(self.d) == ['Accept']\n\n    def test_ci_dict_del(self):\n        del self.d['accept']\n        assert not self.d\n\n    def test_ci_dict_copy_and_equal(self):\n        d = self.d.copy()\n        assert d == self.d\n\n\nclass TestOrderedDefaultDict:\n    def test_default_normal(self):\n        d = OrderedDefaultDict(int)\n        assert d[1] == 0\n        assert d['a'] == 0\n        d[2] = 2\n        assert d[2] == 2\n        assert list(d.keys()) == [1, 'a', 2]\n\n        d = OrderedDefaultDict(int, a=1)\n        assert d['a'] == 1\n\n    def test_default_factory_not_callable(self):\n        with pytest.raises(TypeError):\n            OrderedDefaultDict('notcallable')\n\n    def test_default_factory_none(self):\n        d = OrderedDefaultDict()\n        with pytest.raises(KeyError):\n            d[1]\n\n    def test_copy(self):\n        d1 = OrderedDefaultDict(int, a=[])\n        d2 = d1.copy()\n        assert d2['a'] == []\n        d1['a'].append(1)\n        assert d2['a'] == [1]\n\n    def test_deepcopy(self):\n        import copy\n        d1 = OrderedDefaultDict(int, a=[])\n        d2 = copy.deepcopy(d1)\n        assert d2['a'] == []\n        d1['a'].append(1)\n        assert d2['a'] == []\n\n    def test_repr(self):\n        d = OrderedDefaultDict(int, a=1)\n        assert repr(d).startswith('OrderedDefaultDict')\n\ndef test_attrify():\n    attrd = attrify({\n        'a': [1, 2, {'b': 'b'}],\n        'c': 'c',\n    })\n    assert attrd.a == [1, 2, {'b': 'b'}]\n    assert attrd.a[2].b == 'b'\n    assert attrd.c == 'c'\n\n    attrd = attrify((1, 2))\n    assert attrd == (1, 2)\n\n    attrd = attrify({\n        'a': 1,\n        'b': (1, 2)\n    })\n    assert attrd.a == 1\n    assert attrd.b == (1, 2)\n"
  },
  {
    "path": "tests/test_dt.py",
    "content": "import os\nfrom pydu.dt import timer\n\n\nclass TestTimer(object):\n    def test_context_manager(self):\n        timeit = timer()\n\n        with timeit:\n            os.getcwd()\n\n        assert timeit.elapsed is not None\n\n    def test_decorator(self):\n        timeit = timer()\n\n        @timeit\n        def foo():\n            os.getcwd()\n\n        foo()\n        assert timeit.elapsed is not None\n\n    def test_print_func(self):\n        import sys\n        timeit = timer(print_func=sys.stdout.write)\n\n        with timeit:\n            os.getcwd()\n\n        assert timeit.elapsed is not None\n"
  },
  {
    "path": "tests/test_environ.py",
    "content": "import os\nfrom pydu.environ import environ, path\n\n\ndef test_environ():\n    os.environ['c'] = 'c'\n    with environ(a='a', b='', c=None, d=None):\n        assert os.environ['a'] == 'a'\n        assert os.environ['b'] == ''\n        assert 'c' not in os.environ\n        assert 'd' not in os.environ\n    assert 'a' not in os.environ\n    assert 'b' not in os.environ\n    assert 'c' in os.environ\n    assert 'd' not in os.environ\n\n\ndef test_path():\n    with path(append='foo', prepend='boo'):\n        assert os.environ['PATH'].endswith(os.pathsep + 'foo')\n        assert os.environ['PATH'].startswith('boo' + os.pathsep)\n    assert not os.environ['PATH'].endswith(os.pathsep + 'foo')\n    assert not os.environ['PATH'].startswith('boo' + os.pathsep)\n\n    with path(append='foo', prepend='boo', replace='replace'):\n        assert os.environ['PATH'] == 'replace'\n    assert os.environ['PATH'] != 'replace'\n"
  },
  {
    "path": "tests/test_exception.py",
    "content": "from pydu.exception import ignore, default_if_except\n\n\ndef test_ignore():\n    with ignore(ValueError, AttributeError):\n        int('abc')\n        int.no_exists_func()\n\n\ndef test_default_if_except():\n    @default_if_except(ValueError, default=0)\n    def foo(value):\n        return int(value)\n\n    assert foo('abc') == 0\n    assert foo('1') == 1\n"
  },
  {
    "path": "tests/test_functional.py",
    "content": "from pydu.functional import compose\n\n\ndef test_compose():\n    def f1(a, b=1):\n        return a+b\n\n    def f2(a):\n        return 2*a\n\n    def f3(a, b=3):\n        return a+b\n\n    assert compose(f1, f2, f3)(1) == 9\n    assert compose(f1, f2, f3)(1, b=5) == 13\n"
  },
  {
    "path": "tests/test_inspect.py",
    "content": "from pydu.inspect import (get_func_args, get_func_full_args, func_accepts_var_args,\n                          func_accepts_kwargs, func_supports_parameter)\n\n\nclass Person:\n    def no_arguments(self):\n        return None\n\n    def one_argument(self, something):\n        return something\n\n    def just_args(self, *args):\n        return args\n\n    def just_kwargs(self, **kwargs):\n        return kwargs\n\n    def all_kinds(self, name, address='home', age=25, *args, **kwargs):\n        return kwargs\n\n\ndef func_no_arguments():\n    pass\n\n\ndef func_one_argument(something):\n    pass\n\n\ndef func_just_args(*args):\n    pass\n\n\ndef func_just_kwargs(**kwargs):\n    pass\n\n\ndef func_all_kinds(name, address='home', age=25, *args, **kwargs):\n    pass\n\n\ndef test_get_func_args():\n    arguments = ['name', 'address', 'age']\n    assert get_func_args(Person.all_kinds) == arguments\n\n\ndef test_get_func_full_args():\n    # no arguments\n    assert get_func_full_args(Person.no_arguments) == []\n    assert get_func_full_args(func_no_arguments) == []\n    # one argument\n    assert get_func_full_args(Person.one_argument) == [('something',)]\n    assert get_func_full_args(func_one_argument) == [('something',)]\n    # all_arguments\n    arguments = [('name',), ('address', 'home'), ('age', 25), ('*args',), ('**kwargs',)]\n    assert get_func_full_args(Person.all_kinds) == arguments\n    assert get_func_full_args(func_all_kinds) == arguments\n\n\ndef test_func_accepts_var_args():\n    # has args\n    assert func_accepts_var_args(Person.just_args)\n    assert func_accepts_var_args(func_just_args)\n    # no args\n    assert not func_accepts_var_args(Person.one_argument)\n    assert not func_accepts_var_args(func_one_argument)\n\n\ndef test_func_accepts_kwargs():\n    # has kwargs\n    assert func_accepts_kwargs(Person.just_kwargs)\n    assert func_accepts_kwargs(func_just_kwargs)\n    # no kwargs\n    assert not func_accepts_kwargs(Person.one_argument)\n    assert not func_accepts_kwargs(func_one_argument)\n\n\ndef test_func_supports_parameter():\n    for all_kinds in Person.all_kinds, func_all_kinds:\n        assert func_supports_parameter(all_kinds, 'name')\n        assert func_supports_parameter(all_kinds, 'kwargs')\n        assert not func_supports_parameter(all_kinds, 'self')"
  },
  {
    "path": "tests/test_iter.py",
    "content": "import pytest\nfrom pydu.iter import first, last, all, any, join\n\n\n@pytest.mark.parametrize(\n    'iterable', (\n        [1, 2],\n        (1, 2),\n        {1, 2},\n        {1: 1, 2: 2},\n        iter([1, 2])\n    ))\ndef test_first_last(iterable):\n    assert first(iterable) == 1\n    assert last(iterable) == 2\n\n\ndef test_all():\n    assert all([0, 1, 2], lambda x: x+1)\n    assert not all([0, 1, 2], lambda x: x)\n\n\ndef test_any():\n    assert any([-1, -1, 0], lambda x: x+1)\n    assert not any([-1, -1, -1], lambda x: x + 1)\n\n\ndef test_join():\n    assert join(iter([1, '2', 3])) == '123'\n    assert join(iter([1, '2', 3]), separator=',') == '1,2,3'\n"
  },
  {
    "path": "tests/test_list.py",
    "content": "import pytest\nfrom pydu.list import uniq, tolist, flatten\n\n\ndef test_uniq():\n    assert uniq([1, 4, 0, 2, 0, 3]) == [1, 4, 0, 2, 3]\n\n\n@pytest.mark.parametrize('obj', ('foo', ['foo']))\ndef test_tolist(obj):\n    assert tolist(obj) == ['foo']\n\n\ndef test_flatten():\n    assert list(flatten([1, 2])) == [1, 2]\n    assert list(flatten([1, [2, 3]])) == [1, 2, 3]\n    assert list(flatten([1, [2, [3, 4]]])) == [1, 2, 3, 4]\n"
  },
  {
    "path": "tests/test_misc.py",
    "content": "import sys\nimport time\nimport pytest\nfrom pydu.misc import (trace, TimeoutError, timeout,\n                       memoize, memoize_when_activated,\n                       super_len)\n\ntry:\n    from cStringIO import StringIO  # py2\nexcept ImportError:\n    from io import StringIO  # py3\n\n\ndef test_timeout():\n    @timeout(1)\n    def f1():\n        time.sleep(0.01)\n        return 1\n\n    @timeout(0.01)\n    def f2():\n        time.sleep(1)\n        return 2\n\n    assert f1() == 1\n    with pytest.raises(TimeoutError):\n        f2()\n\n\ndef test_memoize():\n    @memoize\n    def foo(*args, **kwargs):\n        \"\"\"foo docstring\"\"\"\n        calls.append(None)\n        return (args, kwargs)\n\n    calls = []\n    # no args\n    for x in range(2):\n        ret = foo()\n        expected = ((), {})\n        assert ret == expected\n        assert len(calls) == 1\n\n    # with args\n    for x in range(2):\n        ret = foo(1)\n        expected = ((1,), {})\n        assert ret == expected\n        assert len(calls) == 2\n\n    # with args + kwargs\n    for x in range(2):\n        ret = foo(1, bar=2)\n        expected = ((1,), {'bar': 2})\n        assert ret == expected\n        assert len(calls) == 3\n\n    # clear cache\n    foo.cache_clear()\n    ret = foo()\n    expected = ((), {})\n    assert ret == expected\n    assert len(calls) == 4\n\n    # docstring\n    assert foo.__doc__ == \"foo docstring\"\n\n\ndef test_memoize_when_activated():\n    class Foo:\n        @memoize_when_activated\n        def foo(self):\n            calls.append(None)\n\n    f = Foo()\n    calls = []\n    f.foo()\n    f.foo()\n    assert len(calls) == 2\n\n    # activate\n    calls = []\n    f.foo.cache_activate()\n    f.foo()\n    f.foo()\n    assert len(calls) == 1\n\n    # deactivate\n    calls = []\n    f.foo.cache_deactivate()\n    f.foo()\n    f.foo()\n    assert len(calls) == 2\n\n\nclass TestSuperLen:\n    @pytest.mark.parametrize(\n        'stream, value', (\n            (StringIO, 'Test'),\n        ))\n    def test_io_streams(self, stream, value):\n        \"\"\"Ensures that we properly deal with different kinds of IO streams.\"\"\"\n        assert super_len(stream()) == 0\n        assert super_len(stream(value)) == 4\n\n    def test_super_len_correctly_calculates_len_of_partially_read_file(self):\n        \"\"\"Ensure that we handle partially consumed file like objects.\"\"\"\n        s = StringIO()\n        s.write('foobarbogus')\n        assert super_len(s) == 0\n\n    @pytest.mark.parametrize('error', [IOError, OSError])\n    def test_super_len_handles_files_raising_weird_errors_in_tell(self, error):\n        \"\"\"If tell() raises errors, assume the cursor is at position zero.\"\"\"\n\n        class BoomFile(object):\n            def __len__(self):\n                return 5\n\n            def tell(self):\n                raise error()\n\n        assert super_len(BoomFile()) == 0\n\n    @pytest.mark.parametrize('error', [IOError, OSError])\n    def test_super_len_tell_ioerror(self, error):\n        \"\"\"Ensure that if tell gives an IOError super_len doesn't fail\"\"\"\n\n        class NoLenBoomFile(object):\n            def tell(self):\n                raise error()\n\n            def seek(self, offset, whence):\n                pass\n\n        assert super_len(NoLenBoomFile()) == 0\n\n    def test_string(self):\n        assert super_len('Test') == 4\n\n    @pytest.mark.parametrize(\n        'mode, warnings_num', (\n            ('r', 0),\n            ('rb', 0),\n        ))\n    def test_file(self, tmpdir, mode, warnings_num, recwarn):\n        file_obj = tmpdir.join('test.txt')\n        file_obj.write('Test')\n        with file_obj.open(mode) as fd:\n            assert super_len(fd) == 4\n        assert len(recwarn) == warnings_num\n\n    def test_super_len_with__len__(self):\n        foo = [1, 2, 3, 4]\n        len_foo = super_len(foo)\n        assert len_foo == 4\n\n    def test_super_len_with_no__len__(self):\n        class LenFile(object):\n            def __init__(self):\n                self.len = 5\n\n        assert super_len(LenFile()) == 5\n\n    def test_super_len_with_tell(self):\n        foo = StringIO('12345')\n        assert super_len(foo) == 5\n        foo.read(2)\n        assert super_len(foo) == 3\n\n    def test_super_len_with_fileno(self):\n        with open(__file__, 'rb') as f:\n            length = super_len(f)\n            file_data = f.read()\n        assert length == len(file_data)\n\n    def test_super_len_with_no_matches(self):\n        \"\"\"Ensure that objects without any length methods default to 0\"\"\"\n        assert super_len(object()) == 0\n"
  },
  {
    "path": "tests/test_network.py",
    "content": "import pytest\nfrom pydu.network import (dotted_netmask, is_ipv4, is_ipv6, get_free_port,\n                          ip2int, int2ip)\n\n\n@pytest.mark.parametrize(\n    'mask, expected', (\n        (8, '255.0.0.0'),\n        (24, '255.255.255.0'),\n        (25, '255.255.255.128'),\n    ))\ndef test_dotted_netmask(mask, expected):\n    assert dotted_netmask(mask) == expected\n\n\nclass TestIsIPv4Address:\n\n    def test_valid(self):\n        assert is_ipv4('8.8.8.8')\n\n    @pytest.mark.parametrize('value', ('8.8.8.8.8', 'localhost.localdomain'))\n    def test_invalid(self, value):\n        assert not is_ipv4(value)\n\n\nclass TestIsIPv6Address:\n\n    def test_valid(self):\n        assert is_ipv6('fe80::9e5b:b149:e187:1a18')\n\n    @pytest.mark.parametrize('value', ('fe80::9e5b:b149:e187::', 'localhost.localdomain'))\n    def test_invalid(self, value):\n        assert not is_ipv6(value)\n\n\ndef test_get_free_port():\n    port = get_free_port()\n    assert isinstance(port, int)\n    assert 65536 > port > 0\n\n\ndef test_ip2int():\n    assert ip2int('10.1.1.1') == 167837953\n    with pytest.raises(ValueError):\n        ip2int('255.255.255.256')\n\n    assert ip2int('fe80::9e5b:b149:e187:1a18') == 338288524927261089665429805853095434776\n    with pytest.raises(ValueError):\n        ip2int('fe80::9e5b:b149:e187::')\n\n\ndef test_int2ip():\n    assert int2ip(167837953) == '10.1.1.1'\n    assert int2ip(338288524927261089665429805853095434776) == 'fe80::9e5b:b149:e187:1a18'\n    with pytest.raises(ValueError):\n        int2ip(10**50)\n"
  },
  {
    "path": "tests/test_path.py",
    "content": "import os\nimport pytest\nfrom pydu.platform import WINDOWS\nfrom pydu.path import cd, is_super_path, normjoin, filename, fileext\n\n\ndef test_cd(tmpdir):\n    path = str(tmpdir)\n    cwd = os.getcwd()\n    with cd(path):\n        assert os.getcwd() == path\n    assert os.getcwd() == cwd\n\n\nclass TestIsSupoerPath:\n    def test_is_super_path_general(self):\n        assert is_super_path('/aa/bb/cc', '/aa/bb/cc')\n        assert is_super_path('/aa/bb', '/aa/bb/cc')\n        assert is_super_path('/aa', '/aa/bb/cc')\n        assert is_super_path('/', '/aa/bb/cc')\n        assert is_super_path('/', '/')\n        assert not is_super_path('/a', '/aa/bb/cc')\n\n    @pytest.mark.skipif(not WINDOWS, reason='Not support on none-windows')\n    def test_is_super_path_win(self):\n        assert is_super_path('c:/aa/bb', 'c:/aa/bb\\\\cc')\n        assert is_super_path('c:/aa/bb', 'c:/aa\\\\bb/cc')\n        assert is_super_path('c:/aa\\\\bb', 'c:\\\\aa/bb/cc')\n        assert is_super_path('c:/', 'c:\\\\')\n\n\ndef test_normjoin():\n    if WINDOWS:\n        assert normjoin('C:\\\\', 'b') == 'C:\\\\b'\n        assert normjoin('C:\\\\', '\\\\b') == 'C:\\\\b'\n        assert normjoin('C:\\\\a', '\\\\b') == 'C:\\\\b'\n        assert normjoin('C:\\\\a', '..\\\\b') == 'C:\\\\b'\n    else:\n        assert normjoin('/a', 'b') == '/a/b'\n        assert normjoin('/a', '/b') == '/b'\n        assert normjoin('/a', '../b') == '/b'\n\n\ndef test_filename():\n    assert filename('/foo/bar') == 'bar'\n    assert filename('/foo/bar.ext') == 'bar'\n    assert filename('/foo/bar.more.ext') == 'bar.more'\n\n\ndef test_fileext():\n    assert fileext('/foo/bar') == ''\n    assert fileext('/foo/bar.ext') == '.ext'\n    assert fileext('/foo/bar.more.ext') == '.ext'\n"
  },
  {
    "path": "tests/test_platform.py",
    "content": "from pydu.platform import (WINDOWS, LINUX, POSIX, DARWIN, SUNOS, SMARTOS,\n                           FREEBSD, NETBSD, OPENBSD, AIX)\n\n\ndef test_platform_constants():\n    assert any([WINDOWS, LINUX, POSIX, DARWIN, SUNOS, SMARTOS,\n                FREEBSD, NETBSD, OPENBSD, AIX])\n"
  },
  {
    "path": "tests/test_request.py",
    "content": "import socket\nfrom .testing import mockserver\nimport pydu.request\nfrom pydu.network import get_free_port\nfrom pydu.request import FileName, check_connect, update_query_params, cookies_str_to_dict\n\n\ndef test_filename_from_url():\n    url = 'http://www.example.com/test.txt'\n    assert FileName.from_url(url) == 'test.txt'\n\n    url = 'http://www.example.com/'\n    assert FileName.from_url(url) is None\n\n\ndef test_filename_from_headers():\n    headers = {'Content-Disposition': 'attachment; filename=test.txt'}\n    assert FileName.from_headers(headers) == 'test.txt'\n\n    headers = [('Content-Disposition', 'attachment; filename=test.txt')]\n    assert FileName.from_headers(headers) == 'test.txt'\n\n    headers = 'Content-Disposition: attachment; filename=test.txt'\n    assert FileName.from_headers(headers) == 'test.txt'\n\n    headers = 'Content-Disposition: attachment; filename=abc/test.txt'\n    assert FileName.from_headers(headers) == 'test.txt'\n\n    headers = ''\n    assert FileName.from_headers(headers) is None\n\n    headers = 'Content-Disposition: abc'\n    assert FileName.from_headers(headers) is None\n\n    headers = 'Content-Disposition: abc;'\n    assert FileName.from_headers(headers) is None\n\n    headers = 'Content-Disposition: attachment; filename=test.txt; filename=test2.txt'\n    assert FileName.from_headers(headers) is None\n\n    headers = 'Content-Disposition: attachment; filename='\n    assert FileName.from_headers(headers) is None\n\n\n@mockserver\ndef test_check_connect(port=None):\n    assert check_connect('127.0.0.1', port=port, timeout=0.01)\n    assert not check_connect('127.0.0.1', port=get_free_port(), timeout=0.01)\n\n    def mock_socket(*args):\n        raise socket.error\n\n    old_socket = pydu.request.socket.socket\n    pydu.request.socket.socket = mock_socket\n    assert not check_connect('127.0.0.1', port=port, timeout=0.01)\n    pydu.request.socket.socket = old_socket\n\n\ndef test_update_query_params():\n    base = 'http://example.com/'\n    assert update_query_params(base, {'foo': 1}) == base + '?foo=1'\n    assert update_query_params(base + '?foo=1', {'foo': 2}) == base + '?foo=2'\n    assert update_query_params(base + '?foo=1', {'foo': 2, 'bar': 3}) in \\\n           (base + '?foo=2&bar=3', base + '?bar=3&foo=2')\n\n\ndef test_cookies_str_to_dict():\n    cookies = cookies_str_to_dict('a=a;  \\tb=b;\\nc=c;d;e=')\n    assert cookies['a'] == 'a'\n    assert cookies['b'] == 'b'\n    assert cookies['c'] == 'c'\n    assert cookies['e'] == ''\n"
  },
  {
    "path": "tests/test_set.py",
    "content": "from pydu.set import OrderedSet\n\n\ndef test_ordered_set():\n    ordered_set = OrderedSet([1, 3, 1, 2])\n    assert list(ordered_set) == [1, 3, 2]\n    assert 1 in ordered_set\n    assert bool(ordered_set)\n\n    ordered_set.add(1)\n    assert 1 in ordered_set\n\n    ordered_set.remove(1)\n    assert 1 not in ordered_set\n\n    for i in range(4):\n        ordered_set.discard(i)\n    assert not bool(ordered_set)\n"
  },
  {
    "path": "tests/test_slot.py",
    "content": "from pydu.slot import SlotBase\n\n\nclass Foo(SlotBase):\n    __slots__ = ('a', 'b', 'c')\n\n\nclass TestSlotBase(object):\n    def test_args(self):\n        foo = Foo(1)\n        assert foo.a == 1\n        assert foo.b is None\n        assert foo.c is None\n\n    def test_kwargs(self):\n        foo = Foo(b=2)\n        assert foo.a is None\n        assert foo.b == 2\n        assert foo.c is None\n\n    def test_args_kwargs(self):\n        foo = Foo(1, b=2)\n        assert foo.a == 1\n        assert foo.b == 2\n        assert foo.c is None\n"
  },
  {
    "path": "tests/test_string.py",
    "content": "# coding: utf-8\nfrom pydu.string import (safeencode, safeunicode, strips, lstrips, rstrips,\n                         common_prefix, common_suffix, sort)\n\n\ndef test_safeencode():\n    assert safeencode('hello') == b'hello'\n    assert safeencode(1) == b'1'\n    assert safeencode(u'中文') == b'\\xe4\\xb8\\xad\\xe6\\x96\\x87'\n\n\ndef test_safeunicode():\n    assert safeunicode('hello') == u'hello'\n    assert safeunicode(1) == u'1'\n    assert safeunicode('中文') == u'中文'\n\n    assert safeunicode(u'hello') == u'hello'\n    assert safeunicode(u'中文') == u'中文'\n\n\ndef test_lstrips():\n    assert lstrips('foobbar', '') == 'foobbar'\n    assert lstrips('foobar', 'fo') == 'obar'\n    assert lstrips('foofoobar', 'foo') == 'foobar'\n    assert lstrips('foobarbaz', ('foo', 'bar')) == 'baz'\n    assert lstrips('foobarbaz', ('bar', 'foo')) == 'barbaz'\n\n\ndef test_rstrips():\n    assert rstrips('foobbar', '') == 'foobbar'\n    assert rstrips('foobbar', 'bar') == 'foob'\n    assert rstrips('foobarbar', 'bar') == 'foobar'\n    assert rstrips('fozfoobar', ('bar', 'foo')) == 'foz'\n    assert rstrips('fozfoobar', ('foo', 'bar')) == 'fozfoo'\n\n\ndef test_strips():\n    assert strips('foobarfoo', '') == 'foobarfoo'\n    assert strips('foobarfoo', 'foo') == 'bar'\n    assert strips('foobarfoo', ('foo', 'bar')) == ''\n\n\ndef test_common_prefix():\n    l = ['abcd', 'abc1']\n    assert common_prefix(l) == 'abc'\n\n\ndef test_common_suffix():\n    l = ['dabc', '1abc']\n    assert common_suffix(l) == 'abc'\n\n\ndef test_sort():\n    assert sort('acb21') == '12abc'\n    assert sort('abc21', reverse=True) == 'cba21'\n"
  },
  {
    "path": "tests/test_system.py",
    "content": "import os\nimport stat\nimport time\nimport pytest\n\nfrom pydu.platform import WINDOWS\nfrom pydu.system import (FileTracker,\n                         makedirs, remove, removes, open_file, copy, touch,\n                         chmod, which)\n\nif not WINDOWS:\n    from pydu.system import link, symlink\n\n\nclass TestFileTracker:\n    def test_track_open(self, tmpdir):\n        FileTracker.track()\n        path = tmpdir.join('test').strpath\n        f = open(path, 'w')\n        assert f in FileTracker.get_openfiles()\n        f.close()\n        assert f not in FileTracker.get_openfiles()\n\n    def test_track_context_open(self, tmpdir):\n        FileTracker.track()\n        path = tmpdir.join('test').strpath\n        with open(path, 'w') as f:\n            assert f in FileTracker.get_openfiles()\n        assert f not in FileTracker.get_openfiles()\n\n    def test_untrack(self, tmpdir):\n        FileTracker.track()\n        FileTracker.untrack()\n        path = tmpdir.join('test').strpath\n        f = open(path, 'w')\n        assert f not in FileTracker.get_openfiles()\n\n\nclass TestMakeDirs:\n    def test_makedirs(self, tmpdir):\n        path = str(tmpdir.join('test'))\n        makedirs(path)\n        assert os.path.exists(path)\n\n    def test_makedirs_with_exists_path(self, tmpdir):\n        path = str(tmpdir.join('test'))\n        makedirs(path)\n        makedirs(path, exist_ok=True)\n        with pytest.raises(Exception):\n            makedirs(path, exist_ok=False)\n\n    def test_makedirs_with_ignore_error(self, tmpdir):\n        path = str(tmpdir.join('test'))\n        makedirs(path)\n        makedirs(path, ignore_errors=True)\n\n    def test_makedirs_without_ignore_error(self, tmpdir):\n        path = str(tmpdir.join('test'))\n        makedirs(path)\n        with pytest.raises(Exception):\n            makedirs(path, ignore_errors=False, exist_ok=False)\n\n    def test_makedirs_with_mutl_dirs(self, tmpdir):\n        path = str(tmpdir.join('test/test'))\n        makedirs(path)\n        assert os.path.exists(path)\n\n\ndef test_touch(tmpdir):\n    path = str(tmpdir.join('test'))\n    touch(path)\n    assert os.path.isfile(path)\n\n\nclass TestRemove:\n    def test_remove_dir(self, tmpdir):\n        path = str(tmpdir.join('test'))\n        makedirs(path)\n        remove(path)\n        assert not os.path.exists(path)\n\n    def test_remove_file(self, tmpdir):\n        f = str(tmpdir.join('test.txt'))\n        touch(f)\n        remove(f)\n        assert not os.path.exists(f)\n\n    def test_remove_mutil_dirs(self, tmpdir):\n        path = str(tmpdir.join('test/test'))\n        makedirs(path)\n        path = str(tmpdir.join('test'))\n        remove(path)\n        assert not os.path.exists(path)\n\n    def test_remove_with_ignore_error(self, tmpdir):\n        path = str(tmpdir.join('test'))\n        remove(path, ignore_errors=True)\n\n    def test_remove_without_ignore_error(self, tmpdir):\n        path = str(tmpdir.join('test'))\n        with pytest.raises(Exception):\n            remove(path, ignore_errors=False)\n\n    def test_remove_without_ignore_error_with_onerror(self):\n        pass\n\n\nclass TestRemoves:\n    def test_removes_paths(self, tmpdir):\n        path1 = str(tmpdir.join('test1'))\n        path2 = str(tmpdir.join('test2'))\n        path3 = str(tmpdir.join('test3'))\n        for path in [path1, path2, path3]:\n            makedirs(path)\n        removes([path1, path2, path3])\n        assert not os.path.exists(path1)\n        assert not os.path.exists(path2)\n        assert not os.path.exists(path3)\n\n    def test_removes_files(self, tmpdir):\n        f1 = str(tmpdir.join('test1.txt'))\n        f2 = str(tmpdir.join('test2.txt'))\n        f3 = str(tmpdir.join('test3.txt'))\n        for f in [f1, f2, f3]:\n            touch(f)\n        removes([f1, f2, f3])\n        for f in [f1, f2, f3]:\n            assert not os.path.exists(f)\n\n    def test_removes_files_and_path(self, tmpdir):\n        f1 = str(tmpdir.join('test1.txt'))\n        f2 = str(tmpdir.join('test2.txt'))\n        p1 = str(tmpdir.join('test1'))\n        p2 = str(tmpdir.join('test2'))\n        for f in [f1, f2]:\n            touch(f)\n        for p in [p1, p2]:\n            makedirs(p)\n        removes([f1, f2, p1, p2])\n        for f in [f1, f2, p1, p2]:\n            assert not os.path.exists(f)\n\n\nclass TestOpenFile:\n    def test_open_file_without_parent_dir(self, tmpdir):\n        f = str(tmpdir.join('test/test1.txt'))\n        open_file(f)\n        assert os.path.exists(f)\n\n    def test_open_file_in_exist_path(self, tmpdir):\n        f = str(tmpdir.join('test2.txt'))\n        open_file(f)\n        assert os.path.exists(f)\n\n    def test_open_exist_file(self, tmpdir):\n        f = str(tmpdir.join('test3.txt'))\n        open_file(f)\n        with open(f, 'r') as f:\n            with pytest.raises(Exception):\n                os.remove(f)\n\n    def test_open_file_with_ignore_error(self, tmpdir):\n        f = str(tmpdir.join('test1.txt'))\n        open_file(f, mode='r', ignore_errors=True)\n\n    def test_open_file_without_ignore_error(self, tmpdir):\n        f = str(tmpdir.join('test1.txt'))\n        with pytest.raises(Exception):\n            open_file(f, mode='r')\n\n\n@pytest.mark.skipif(WINDOWS, reason='Not support on windows')\nclass TestLink:\n    def test_link_a_file(self, tmpdir):\n        f = str(tmpdir.join('test.txt'))\n        link_f = str(tmpdir.join('test.link'))\n        touch(f)\n        link(f, link_f)\n        assert os.path.exists(link_f)\n\n    def test_link_with_ignore_error(self, tmpdir):\n        dirname = str(tmpdir.join('test'))\n        link_dirname = str(tmpdir.join('test.link'))\n        makedirs(dirname)\n        link(dirname, link_dirname, ignore_errors=True)\n\n    def test_link_without_ignore_error(self, tmpdir):\n        d = str(tmpdir.join('test'))\n        link_d = str(tmpdir.join('test.link'))\n        makedirs(d)\n        with pytest.raises(Exception):\n            link(d, link_d)\n\n    def test_link_with_overwrite(self, tmpdir):\n        f = str(tmpdir.join('test.txt'))\n        link_f = str(tmpdir.join('test.link'))\n        touch(f)\n        link(f, link_f)\n        t1 = os.path.getctime(link_f)\n        time.sleep(1)\n        link(f, link_f, overwrite=True)\n        t2 = os.path.getctime(link_f)\n        assert t1 != t2\n\n\n@pytest.mark.skipif(WINDOWS, reason='Not support on windows')\nclass TestSymLink:\n    def test_symlink_a_file(self, tmpdir):\n        f = str(tmpdir.join('test.txt'))\n        link_f = str(tmpdir.join('test.link'))\n        touch(f)\n        symlink(f, link_f)\n        assert os.path.exists(link_f)\n        assert os.path.islink(link_f)\n\n    def test_symlink_with_ignore_error(self, tmpdir):\n        d = str(tmpdir.join('test'))\n        link_d = str(tmpdir.join('test.link'))\n        makedirs(d)\n        link(d, link_d, ignore_errors=True)\n\n    def test_symlink_with_overwrite(self, tmpdir):\n        f = str(tmpdir.join('test.txt'))\n        link_f = str(tmpdir.join('test.link'))\n        touch(f)\n        symlink(f, link_f)\n        t1 = os.lstat(link_f).st_ctime\n        time.sleep(1)\n        symlink(f, link_f, overwrite=True)\n        t2 = os.lstat(link_f).st_ctime\n        assert t1 != t2\n\n    def test_symlink_without_ignore_error(self, tmpdir):\n        d = str(tmpdir.join('test'))\n        link_d = str(tmpdir.join('test.link'))\n        makedirs(d)\n        with pytest.raises(Exception):\n            link(d, link_d)\n\n\nclass TestCopy:\n    def test_copy_file(self, tmpdir):\n        f = str(tmpdir.join('test.txt'))\n        f_copy = str(tmpdir.join('test_copy.txt'))\n        touch(f)\n        copy(f, f_copy)\n        assert os.path.exists(f_copy)\n\n    def test_copy_non_empty_dir(self, tmpdir):\n        f = str(tmpdir.join('test/test.txt'))\n        d = str(tmpdir.join('test'))\n        d_copy = str(tmpdir.join('test_copy'))\n        os.makedirs(d)\n        touch(f)\n        copy(d, d_copy)\n        assert os.path.exists(d_copy)\n\n    def test_copy_empty_dir(self, tmpdir):\n        d = str(tmpdir.join('test'))\n        d_copy = str(tmpdir.join('test_copy'))\n        makedirs(d)\n        copy(d, d_copy)\n        assert os.path.exists(d_copy)\n\n    @pytest.mark.skipif(WINDOWS, reason='Not support on windows')\n    def test_copy_dir_follow_symlink(self, tmpdir):\n        f = str(tmpdir.join('test/test.txt'))\n        d = str(tmpdir.join('test'))\n        link_f = str(tmpdir.join('test/test_link.txt'))\n        d_copy = str(tmpdir.join('test_copy'))\n        new_link_f = str(tmpdir.join('test_copy/test_link.txt'))\n        makedirs(d)\n        touch(f)\n        os.symlink(f, link_f)\n        copy(d, d_copy, follow_symlinks=True)\n        assert os.path.exists(d_copy)\n        assert os.path.islink(new_link_f)\n\n    @pytest.mark.skipif(WINDOWS, reason='Not support on windows')\n    def test_copy_dir_not_follow_symlink(self, tmpdir):\n        f = str(tmpdir.join('test/test.txt'))\n        d = str(tmpdir.join('test'))\n        link_f = str(tmpdir.join('test/test_link.txt'))\n        d_copy = str(tmpdir.join('test_copy'))\n        new_link_f = str(tmpdir.join('test_copy/test_link.txt'))\n        makedirs(d)\n        touch(f)\n        os.symlink(f, link_f)\n        copy(d, d_copy, follow_symlinks=False)\n        assert os.path.exists(d_copy)\n        assert not os.path.islink(new_link_f)\n\n    @pytest.mark.skipif(WINDOWS, reason='Not support on windows')\n    def test_copy_file_follow_symlink(self, tmpdir):\n        f = str(tmpdir.join('test.txt'))\n        link_f = str(tmpdir.join('test.link'))\n        copy_link_f = str(tmpdir.join('test_copy.link'))\n        touch(f)\n        link(f, link_f)\n        copy(link_f, copy_link_f, follow_symlinks=True)\n        assert os.path.exists(copy_link_f)\n        assert not os.path.islink(copy_link_f)\n\n    @pytest.mark.skipif(WINDOWS, reason='Not support on windows')\n    def test_copy_file_not_follow_symlink(self, tmpdir):\n        f = str(tmpdir.join('test.txt'))\n        link_f = str(tmpdir.join('test.link'))\n        copy_link_f = str(tmpdir.join('test_copy.link'))\n        touch(f)\n        os.symlink(f, link_f)\n        copy(link_f, copy_link_f, follow_symlinks=False)\n        assert os.path.exists(copy_link_f)\n        assert os.path.islink(copy_link_f)\n\n    def test_copy_path_to_exits_path(self, tmpdir):\n        d1 = str(tmpdir.join('test1'))\n        d2 = str(tmpdir.join('test2'))\n        makedirs(d1)\n        makedirs(d2)\n        with pytest.raises(Exception):\n            copy(d1, d2)\n\n    def test_copy_without_ignore_error(self, tmpdir):\n        d1 = str(tmpdir.join('test1'))\n        d2 = str(tmpdir.join('test2'))\n        makedirs(d1)\n        makedirs(d2)\n        with pytest.raises(Exception):\n            copy(d1, d2, ignore_errors=False)\n\n    def test_copy_with_ignore_error(self, tmpdir):\n        d1 = str(tmpdir.join('test1'))\n        d2 = str(tmpdir.join('test2'))\n        makedirs(d1)\n        makedirs(d2)\n        copy(d1, d2, ignore_errors=True)\n\n\n@pytest.fixture()\ndef mycmd(tmpdir):\n    mycmd = str(tmpdir.join('mycmd'))\n    touch(mycmd)\n    os.chmod(mycmd, os.F_OK | stat.S_IXUSR)\n    os.environ['PATHEXT'] = ''\n    return mycmd\n\n\n@pytest.mark.usefixtures('mycmd')\nclass TestWhich:\n    def test_dir_cmd(self, mycmd):\n        assert which('noexists/mycmd') is None\n        assert which(mycmd) == mycmd\n\n    def test_cmd_path(self, tmpdir, mycmd):\n        path = str(tmpdir)\n        assert which('mycmd') is None\n        assert which('mycmd', path=path) == mycmd\n\n        os.environ['PATH'] = path + os.pathsep + \\\n            os.environ.get('PATH', os.defpath)\n        assert which('mycmd') == mycmd\n\n\n@pytest.mark.skipif(not WINDOWS, reason='Not support non Windows')\ndef test_chcp():\n    from pydu.system import chcp\n    from ctypes import windll\n\n    origin_code = windll.kernel32.GetConsoleOutputCP()\n    with chcp(437):\n        assert windll.kernel32.GetConsoleOutputCP() == 437\n    assert windll.kernel32.GetConsoleOutputCP() == origin_code\n\n    try:\n        cp = chcp(437)\n        assert windll.kernel32.GetConsoleOutputCP() == 437\n        assert str(cp) == '<active code page number: 437>'\n    finally:\n        windll.kernel32.SetConsoleOutputCP(origin_code)\n\n\nclass TestChmod:\n    def test_chmod_file(self, tmpdir):\n        test_file = tmpdir.join('test_file')\n        touch(test_file.strpath)\n        chmod(test_file.strpath, 0o755)\n\n        mode = oct(test_file.stat().mode)[-3:]\n        if WINDOWS:\n            assert mode == '666'\n        else:\n            assert mode == '755'\n\n        chmod(test_file.strpath, 0o444)\n        mode = oct(test_file.stat().mode)[-3:]\n        assert mode == '444'\n\n    def test_chmod_dir(self, tmpdir):\n        test_dir = tmpdir.mkdir('test_dir')\n        test_sub_dir = test_dir.mkdir('test_dir')\n        test_sub_file = test_dir.join('test_file')\n        touch(test_sub_file.strpath)\n\n        if WINDOWS:\n            chmod(test_dir.strpath, 0o444, recursive=False)\n            assert oct(test_dir.stat().mode)[-3:] == '555'\n            assert oct(test_sub_dir.stat().mode)[-3:] != '444'\n            assert oct(test_sub_file.stat().mode)[-3:] != '444'\n\n            chmod(test_dir.strpath, 0o444, recursive=True)\n            assert oct(test_dir.stat().mode)[-3:] == '555'\n            assert oct(test_sub_dir.stat().mode)[-3:] == '555'\n            assert oct(test_sub_file.stat().mode)[-3:] == '444'\n        else:\n            chmod(test_dir.strpath, 0o744, recursive=False)\n            assert oct(test_dir.stat().mode)[-3:] == '744'\n            assert oct(test_sub_dir.stat().mode)[-3:] != '744'\n            assert oct(test_sub_file.stat().mode)[-3:] != '744'\n\n            chmod(test_dir.strpath, 0o744, recursive=True)\n            assert oct(test_dir.stat().mode)[-3:] == '744'\n            assert oct(test_sub_dir.stat().mode)[-3:] == '744'\n            assert oct(test_sub_file.stat().mode)[-3:] == '744'\n"
  },
  {
    "path": "tests/test_unit.py",
    "content": "from pydu.unit import Bytes\n\n\nclass TestBytes:\n    def test_convert(self):\n        assert Bytes(1024*1024).convert() == (1, 'MB')\n        assert Bytes(1024*1024).convert(unit='KB') == (1024, 'KB')\n        assert Bytes(1000).convert(multiple=1000) == (1, 'KB')\n"
  },
  {
    "path": "tests/testing.py",
    "content": "import functools\nfrom threading import Thread\nfrom pydu.network import get_free_port\nfrom pydu.inspect import func_supports_parameter\nfrom pydu.compat import PY2, ClassTypes\n\nif PY2:\n    from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler\nelse:\n    from http.server import HTTPServer as HTTPServer\n    from http.server import BaseHTTPRequestHandler\n\n\nclass mockserverfy(object):\n\n    def __init__(self, RequestHandler=BaseHTTPRequestHandler):\n        self.RequestHandler = RequestHandler\n        self.server = None\n        self.port = None\n\n    def __enter__(self):\n        self.port = get_free_port()\n        self.server = HTTPServer(('127.0.0.1', self.port), self.RequestHandler)\n\n        t = Thread(target=self.server.serve_forever)\n        t.setDaemon(True)\n        t.start()\n        return self\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        self.server.shutdown()\n\n\ndef mockserver(test):\n    \"\"\"A decorator tests that use mock server\"\"\"\n    def decorate_class(klass):\n        for attr in dir(klass):\n            if not attr.startswith('test_'):\n                continue\n\n            attr_value = getattr(klass, attr)\n            if not hasattr(attr_value, \"__call__\"):\n                continue\n\n            setattr(klass, attr, decorate_callable(attr_value))\n        return klass\n\n    def decorate_callable(test):\n        @functools.wraps(test)\n        def wrapper(*args, **kwargs):\n            with mockserverfy() as server:\n                if func_supports_parameter(test, 'port'):\n                    return test(*args, port=server.port, **kwargs)\n                else:\n                    return test(*args, **kwargs)\n        return wrapper\n\n    if isinstance(test, ClassTypes):\n        return decorate_class(test)\n    return decorate_callable(test)\n"
  },
  {
    "path": "tox.ini",
    "content": "[tox]\nenvlist = py27,py35,py36,py37,py38\n\n[testenv]\ndeps=-rrequirements-dev.txt\ncommands=\n    coverage run -m pytest\n"
  }
]