parent
d55ed34256
commit
29853bd5bd
@ -0,0 +1,36 @@
|
|||||||
|
About
|
||||||
|
------
|
||||||
|
|
||||||
|
`Naked <http://naked-py.com>`_ is a new Python command line application framework that is in development. The current release is a stable, testing release.
|
||||||
|
|
||||||
|
Updates
|
||||||
|
--------
|
||||||
|
|
||||||
|
Changes, updates, and brief tutorials are available on the `developer log <http://nakedpy.wordpress.com/>`_.
|
||||||
|
|
||||||
|
QuickStart Guide
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The quickstart guide is available at `http://docs.naked-py.com/quickstart.html <http://docs.naked-py.com/quickstart.html>`_. It demonstrates how the available tools can be incorporated into your development workflow, spanning the entire period from an empty project directory to your first PyPI version release.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Documentation is in development at `http://docs.naked-py.com/ <http://docs.naked-py.com/>`_
|
||||||
|
|
||||||
|
Issue Reporting
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Issue reporting is available on the `GitHub repository <http://github.com/chrissimpkins/naked/issues>`_
|
||||||
|
|
||||||
|
Contribute
|
||||||
|
-----------
|
||||||
|
|
||||||
|
I would greatly appreciate feedback from anyone who is testing the framework. Feel free to contact me on Twitter (@csimpkins) if you have questions, comments, or problems. Please use the issue reporting link above for bug reports.
|
||||||
|
|
||||||
|
Example Application
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
`Status <https://pypi.python.org/pypi/status>`_ was built from the ground up with the Naked Framework. If you'd like to check out the source, it is available on `GitHub <https://github.com/chrissimpkins/status/tree/master/lib/status>`_
|
||||||
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
|||||||
|
Metadata-Version: 2.0
|
||||||
|
Name: Naked
|
||||||
|
Version: 0.1.31
|
||||||
|
Summary: A command line application framework
|
||||||
|
Home-page: http://naked-py.com
|
||||||
|
Author: Christopher Simpkins
|
||||||
|
Author-email: chris@zerolabs.net
|
||||||
|
License: MIT
|
||||||
|
Keywords: python,command line,system,application,framework,CLI
|
||||||
|
Platform: any
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: Natural Language :: English
|
||||||
|
Classifier: License :: OSI Approved :: MIT License
|
||||||
|
Classifier: Operating System :: OS Independent
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 2
|
||||||
|
Classifier: Programming Language :: Python :: 2.6
|
||||||
|
Classifier: Programming Language :: Python :: 2.7
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Programming Language :: Python :: 3.3
|
||||||
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||||
|
Requires-Dist: Naked
|
||||||
|
Requires-Dist: requests
|
||||||
|
Requires-Dist: pyyaml
|
||||||
|
|
||||||
|
About
|
||||||
|
------
|
||||||
|
|
||||||
|
`Naked <http://naked-py.com>`_ is a new Python command line application framework that is in development. The current release is a stable, testing release.
|
||||||
|
|
||||||
|
Updates
|
||||||
|
--------
|
||||||
|
|
||||||
|
Changes, updates, and brief tutorials are available on the `developer log <http://nakedpy.wordpress.com/>`_.
|
||||||
|
|
||||||
|
QuickStart Guide
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The quickstart guide is available at `http://docs.naked-py.com/quickstart.html <http://docs.naked-py.com/quickstart.html>`_. It demonstrates how the available tools can be incorporated into your development workflow, spanning the entire period from an empty project directory to your first PyPI version release.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Documentation is in development at `http://docs.naked-py.com/ <http://docs.naked-py.com/>`_
|
||||||
|
|
||||||
|
Issue Reporting
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Issue reporting is available on the `GitHub repository <http://github.com/chrissimpkins/naked/issues>`_
|
||||||
|
|
||||||
|
Contribute
|
||||||
|
-----------
|
||||||
|
|
||||||
|
I would greatly appreciate feedback from anyone who is testing the framework. Feel free to contact me on Twitter (@csimpkins) if you have questions, comments, or problems. Please use the issue reporting link above for bug reports.
|
||||||
|
|
||||||
|
Example Application
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
`Status <https://pypi.python.org/pypi/status>`_ was built from the ground up with the Naked Framework. If you'd like to check out the source, it is available on `GitHub <https://github.com/chrissimpkins/status/tree/master/lib/status>`_
|
||||||
|
|
||||||
|
|
@ -0,0 +1,118 @@
|
|||||||
|
../../Scripts/naked.exe,sha256=rMRI4H8Qgi_Gxg9r50MNWd2wsP9DnbZLKLpKzLRkRD4,92999
|
||||||
|
Naked-0.1.31.data/scripts/naked,sha256=Ps1mufxMkBG7wwaJHQ_nkMwOrPP1hTZLDH7YMNO4Y2E,285
|
||||||
|
Naked-0.1.31.dist-info/DESCRIPTION.rst,sha256=26S5QwAzVdLRvvgm9ZsFe2VsF2f6xybGklQEMt0T05s,1450
|
||||||
|
Naked-0.1.31.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||||
|
Naked-0.1.31.dist-info/METADATA,sha256=z7IWAlMC3eIlNc8RlWxJ1feuDhdlQtziBOXXzcZK-c8,2341
|
||||||
|
Naked-0.1.31.dist-info/RECORD,,
|
||||||
|
Naked-0.1.31.dist-info/WHEEL,sha256=SXYYsi-y-rEGIva8sB8iKF6bAFD6YDhmqHX5hI3fc0o,110
|
||||||
|
Naked-0.1.31.dist-info/entry_points.txt,sha256=-7nL8pc5fnKhIUINYqLDa1jd3y-8jynn9u5qF9EyjqU,42
|
||||||
|
Naked-0.1.31.dist-info/pydist.json,sha256=cSetsKYPMVxYIilVGZD_wtke5QtnHkWmKGTF5uDSV10,1119
|
||||||
|
Naked-0.1.31.dist-info/top_level.txt,sha256=CG1yk0vViILO5nUzrfUqtOs-0IAsZBY1zLFGi3O-SCc,6
|
||||||
|
Naked/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
Naked/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
Naked/__pycache__/app.cpython-37.pyc,,
|
||||||
|
Naked/__pycache__/commandline.cpython-37.pyc,,
|
||||||
|
Naked/__pycache__/scratchpad.cpython-37.pyc,,
|
||||||
|
Naked/__pycache__/settings.cpython-37.pyc,,
|
||||||
|
Naked/app.py,sha256=ifeYxmcVb3aolzqZNzeQGF6rnTuJatyM3swhf68G5cc,11880
|
||||||
|
Naked/commandline.py,sha256=OJ-ktVQWZeKMTbC1r446M0m1BZidwBQMA5hpXrTMK5U,18918
|
||||||
|
Naked/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
Naked/commands/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/args.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/build.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/classifier.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/dist.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/help.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/locate.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/make.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/profile.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/pyh.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/test.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/usage.cpython-37.pyc,,
|
||||||
|
Naked/commands/__pycache__/version.cpython-37.pyc,,
|
||||||
|
Naked/commands/args.py,sha256=DhW7JF2FNU0ns3P0RrcKM_aN8Pn51zfgLpa7edS7QoE,4681
|
||||||
|
Naked/commands/build.py,sha256=SDs5d6s4aCyT6zKWpJ4CgYnu8VKx910CKRe-E9mFhS8,1040
|
||||||
|
Naked/commands/classifier.py,sha256=NY4eOKDh3xiSJbJfvxN8D-YqjJrqc8qQ9e03vlPAf-A,1875
|
||||||
|
Naked/commands/dist.py,sha256=qGG1l9Z1T882WvRjBKIs-UQWSbN9efaZkK-qSYTTVV4,3287
|
||||||
|
Naked/commands/help.py,sha256=cQzrW2Axb4X_zR4JElmCaWUcE1xk2Sf9VpNO31FAimA,300
|
||||||
|
Naked/commands/locate.py,sha256=9wZ_RSCNju1hWGkrUGDOSlWTPg6cbA4_cKJuAKW4sxs,1449
|
||||||
|
Naked/commands/make.py,sha256=ikbMj3T24LExBaxgd465DBNNemqZb8y6RHHnKJTsQFY,18788
|
||||||
|
Naked/commands/profile.py,sha256=Rz_rWwdqXy6WrWrPaDCBrsVBwpJrwRCrsnHtqj-LmZ0,1700
|
||||||
|
Naked/commands/pyh.py,sha256=Bqfo513IKwPB0xY4YvtUSdvpSIetE9tW-gfTG2VoMqY,1071
|
||||||
|
Naked/commands/test.py,sha256=-T_cXmbS5BoL7wKuC6VxQ0W8-Q-dcRl64fVZsiEj75g,6720
|
||||||
|
Naked/commands/usage.py,sha256=ASp5GkdPHEd9Nz_rYNfjojI0KXmHMhmbs7ktte6LecY,305
|
||||||
|
Naked/commands/version.py,sha256=kSPrr4blhbTST1SNUEKnnD9UrP9UW7s9ehos-qiM5as,789
|
||||||
|
Naked/scratchpad.py,sha256=NJ6NYQrmzVHDHOEagFcACuSp0C2FiyKhHwpZbz7VkXE,743
|
||||||
|
Naked/settings.py,sha256=uXuD_QpSbaOL37N-p_4pk1Fsrb_uQpIl3wkg6uTR8Cw,2976
|
||||||
|
Naked/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
Naked/templates/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
Naked/templates/__pycache__/app_file.cpython-37.pyc,,
|
||||||
|
Naked/templates/__pycache__/licenses.cpython-37.pyc,,
|
||||||
|
Naked/templates/__pycache__/manifest_in_file.cpython-37.pyc,,
|
||||||
|
Naked/templates/__pycache__/profiler_file.cpython-37.pyc,,
|
||||||
|
Naked/templates/__pycache__/pypush_file.cpython-37.pyc,,
|
||||||
|
Naked/templates/__pycache__/readme_md_file.cpython-37.pyc,,
|
||||||
|
Naked/templates/__pycache__/settings_file.cpython-37.pyc,,
|
||||||
|
Naked/templates/__pycache__/setup_cfg_file.cpython-37.pyc,,
|
||||||
|
Naked/templates/__pycache__/setup_py_file.cpython-37.pyc,,
|
||||||
|
Naked/templates/app_file.py,sha256=A874Fi7TBfX2O8uMEcYvNbpUvc15C-rrFbT9MLWOL0Q,5188
|
||||||
|
Naked/templates/licenses.py,sha256=WF2mFsv8oH_xUdEJMY17oWxmjmShOagYBQRdGReIxxE,6052
|
||||||
|
Naked/templates/manifest_in_file.py,sha256=9FkG7ZHdadisE-Uh7qdI69Hn-hfACTo7II8LG23bGBY,109
|
||||||
|
Naked/templates/profiler_file.py,sha256=XSxC4nBng26j7NmmmOXHz2wZ4702FpAGntaNeQ8WGLw,1530
|
||||||
|
Naked/templates/pypush_file.py,sha256=RUndgzH4y9LhYgka1oTvVB__yfvXdysNdK7qOzwONyE,586
|
||||||
|
Naked/templates/readme_md_file.py,sha256=njESHWuw5heC2EjRe8m7BgY1Wv2gXMUSnf0SjXLLpB4,104
|
||||||
|
Naked/templates/settings_file.py,sha256=bkCfQE_qp-tdXCwBdII_4IqaO2U5XJUyh-QvTypOW1s,1179
|
||||||
|
Naked/templates/setup_cfg_file.py,sha256=Bl-AkoQWe2Ath13L68J6pnzVPyz0FRmr7vCKIdyeAkU,107
|
||||||
|
Naked/templates/setup_py_file.py,sha256=Z2c0FpDTKnRP2txJB7fSCEkD17CnyTgIFOLCRlR_kgs,1769
|
||||||
|
Naked/toolshed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
Naked/toolshed/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/benchmarking.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/casts.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/file.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/git.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/ink.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/iter.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/network.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/python.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/shell.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/state.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/system.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/__pycache__/types.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/benchmarking.py,sha256=Dn5EXmR_wiAtG-mqWmBFQJ_i749_-hZ1-PxEBwSsAwQ,17235
|
||||||
|
Naked/toolshed/c/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
Naked/toolshed/c/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/c/__pycache__/setup.cpython-37.pyc,,
|
||||||
|
Naked/toolshed/c/benchmarking.c,sha256=UDYMyo3eaMQB0cc28AfkyePULEQuvtqaFeZWK9DgzLM,880286
|
||||||
|
Naked/toolshed/c/benchmarking.pyx,sha256=Dn5EXmR_wiAtG-mqWmBFQJ_i749_-hZ1-PxEBwSsAwQ,17235
|
||||||
|
Naked/toolshed/c/build.sh,sha256=LEtZwrw9A6Tx4Z-tSf4kjSKFLTyX97xs5uoJBcitB8w,36
|
||||||
|
Naked/toolshed/c/casts.c,sha256=bgEj7BmtKcHmMd1W5qIcOC8_KH5BP66z3x_tLMF4N7I,227012
|
||||||
|
Naked/toolshed/c/casts.pyx,sha256=Jq6Wt3re7fS1-ry9uD4B38yfGi-CbwhxsLZl8HODNis,4813
|
||||||
|
Naked/toolshed/c/cstate.c,sha256=qTe_e46gCtGYUdgt--ulHl9NaE4T_K7j7D5V93AUawA,134375
|
||||||
|
Naked/toolshed/c/cstate.pyx,sha256=5EDxBjneKCRyEwQslZspCLl1A2i7dMtEIu1mfM75qpo,1089
|
||||||
|
Naked/toolshed/c/cythonize.sh,sha256=-Fy5q0oMAqOKj5aFDfRuaQUPpqGrKCSDatPdFt0d_zI,256
|
||||||
|
Naked/toolshed/c/file.c,sha256=DtLXGU5JzSJCDzC8tZxM0k_ucf3ONwepxB6Q1fipL1I,623270
|
||||||
|
Naked/toolshed/c/file.pyx,sha256=ru3r-7QVgTubZ3sbbNO_jqF0o_qfJBYzfGSXPL7X18c,19965
|
||||||
|
Naked/toolshed/c/ink.c,sha256=L0brN1oD-airTLFV378wfkLZ6r1MgAHozsl5GlaMjdo,185313
|
||||||
|
Naked/toolshed/c/ink.pyx,sha256=r73b8N46boGa7Cerw88uPGk3luNQWgoE7SS2Ru4k0ys,5460
|
||||||
|
Naked/toolshed/c/network.c,sha256=ft-bqnr3ervphD1IXiXN9krqZ6GJPmLA2OdrH9N8CaE,525661
|
||||||
|
Naked/toolshed/c/network.pyx,sha256=-NX-7RFbN2DAsOdW4mvtNYnCP1GVHx8CQubmL7wjaQg,19549
|
||||||
|
Naked/toolshed/c/python.c,sha256=E2rAQDq1pVgehMwDQenNF5ZZIBdpauHKA5TKFPWxBSk,111860
|
||||||
|
Naked/toolshed/c/python.pyx,sha256=WTST5lYVqizCtAXbHZqd5HGOO9ywCCxP8dbzLB2tbcM,2157
|
||||||
|
Naked/toolshed/c/setup.py,sha256=oOuer8J1ZqJxxWhXiYzmtPmyeaQy2FNQNp1c99AzF4U,654
|
||||||
|
Naked/toolshed/c/shell.c,sha256=1CrgdwUQsHkkzKKvVYfmZoA6OaG23xqriKiO0a_secI,376470
|
||||||
|
Naked/toolshed/c/shell.pyx,sha256=efpvnnT8cu6xXOGvhqd2MpMCiv2Z4Eau8VjRp9pAYSc,12629
|
||||||
|
Naked/toolshed/c/system.c,sha256=bSRfuYZe8C5nIk2WZrcXm5eYeJZ-3OwD5gzx2jwUPtE,620280
|
||||||
|
Naked/toolshed/c/system.pyx,sha256=bPjCcxkWLuS-X5CZgUC8Rkilc2x-S-X2e9MkvzEf-xw,24684
|
||||||
|
Naked/toolshed/c/types.c,sha256=STvR0OE_PLkGros1B0vjepCZdhClEjjaCEkqyuZNMOc,1240797
|
||||||
|
Naked/toolshed/c/types.pyx,sha256=ep0NkR_OFbmp96KaAx5mxPYYUTtc7V6L1qSkaLS_oq8,44721
|
||||||
|
Naked/toolshed/casts.py,sha256=elaNlp0hBRJ_bFPTX-4eYB90gmPrbVPz_6TY4HFrbc8,5126
|
||||||
|
Naked/toolshed/file.py,sha256=FnYhoVCgnTYU-WjedCLJFgsd9UkvbvJNt5J-ftWML3E,19941
|
||||||
|
Naked/toolshed/git.py,sha256=WAX_snuOFpVIIBPRsIZXva24wK67MEBNZE5eST4fSDg,79
|
||||||
|
Naked/toolshed/ink.py,sha256=9i_ZJpgpXtLqDs32OFGoeiNU7Ajo7xwKmI9FdCCcMt8,5436
|
||||||
|
Naked/toolshed/iter.py,sha256=yUalH_tyXYtEqsFDuy2n5ZfussXmdFunzgA4W7Ydz9M,500
|
||||||
|
Naked/toolshed/network.py,sha256=VpTSm6XQy2Pm_-atsqyt5vd9ZMnYFrJsfJhPwZ2R-js,19525
|
||||||
|
Naked/toolshed/python.py,sha256=lY-Nb8nymApIyI4pWJNHst11VA8IJygBF62ASnjg9Yk,2158
|
||||||
|
Naked/toolshed/shell.py,sha256=8hAdzS15TTzofHr2mRCLpf7ThTNiDVouQjWI18LSCNg,12605
|
||||||
|
Naked/toolshed/state.py,sha256=_PnDMY5Vws9JXN6VdoQlNWwhZ1EwoiwUDu-qjl_Nmk4,1061
|
||||||
|
Naked/toolshed/system.py,sha256=IeJhooglhxKbKiG5NYw6_ye8MeRKNSY5nOz1BZCZBNk,24660
|
||||||
|
Naked/toolshed/types.py,sha256=xd_Bu_fKu0FbULD4EvwQ_OBYWMuBVj5r4pEx4Wm7riA,44532
|
@ -0,0 +1,6 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: bdist_wheel (0.22.0)
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py2-none-any
|
||||||
|
Tag: py3-none-any
|
||||||
|
|
@ -0,0 +1,3 @@
|
|||||||
|
[console_scripts]
|
||||||
|
naked = Naked.app:main
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
{"license": "MIT", "exports": {"console_scripts": {"naked": "Naked.app:main"}}, "document_names": {"description": "DESCRIPTION.rst"}, "name": "Naked", "metadata_version": "2.0", "contacts": [{"role": "author", "email": "chris@zerolabs.net", "name": "Christopher Simpkins"}], "generator": "bdist_wheel (0.22.0)", "commands": {"wrap_console": {"naked": "Naked.app:main"}}, "summary": "A command line application framework", "project_urls": {"Home": "http://naked-py.com"}, "platform": "any", "run_requires": [{"requires": ["Naked", "requests", "pyyaml"]}], "version": "0.1.31", "keywords": "python,command line,system,application,framework,CLI", "classifiers": ["Intended Audience :: Developers", "Natural Language :: English", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Topic :: Software Development :: Libraries :: Python Modules"], "extras": []}
|
@ -0,0 +1 @@
|
|||||||
|
Naked
|
@ -0,0 +1,229 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Naked | A Python command line application framework
|
||||||
|
# Copyright 2014 Christopher Simpkins
|
||||||
|
# MIT License
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------
|
||||||
|
# c.cmd = Primary command (<executable> <primary command>)
|
||||||
|
# c.cmd2 = Secondary command (<executable> <primary command> <secondary command>)
|
||||||
|
#
|
||||||
|
# c.option(option_string, [bool argument_required]) = test for option with optional test for positional arg to the option
|
||||||
|
# c.option_with_arg(option_string) = test for option and mandatory positional argument to option test
|
||||||
|
# c.flag(flag_string) = test for presence of a "--option=argument" style flag
|
||||||
|
#
|
||||||
|
# c.arg(arg_string) = returns the next positional argument to the arg_string argument
|
||||||
|
# c.flag_arg(flag_string) = returns the flag assignment for a "--option=argument" style flag
|
||||||
|
#------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Application start
|
||||||
|
def main():
|
||||||
|
import sys
|
||||||
|
from Naked.commandline import Command
|
||||||
|
#from Naked.toolshed.state import StateObject
|
||||||
|
from Naked.toolshed.system import stderr
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ Instantiate command line object ]
|
||||||
|
# used for all subsequent conditional logic in the CLI application
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
c = Command(sys.argv[0], sys.argv[1:])
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ Instantiate state object ]
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#state = StateObject()
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ Command Suite Validation ] - early validation of appropriate command syntax
|
||||||
|
# Test that user entered a primary command, print usage if not
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
if not c.command_suite_validates():
|
||||||
|
from Naked.commands.usage import Usage
|
||||||
|
Usage().print_usage()
|
||||||
|
sys.exit(1)
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ PRIMARY COMMAND LOGIC ]
|
||||||
|
# Test for primary commands and handle them
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ args ] - identify the parsed arguments for a command string (2)= help
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
if c.cmd == "args":
|
||||||
|
if c.cmd2 == "help":
|
||||||
|
from Naked.commands.args import help as args_help
|
||||||
|
args_help()
|
||||||
|
elif c.argc > 0: # there is an argument to where that is not help
|
||||||
|
from Naked.commands.args import Args
|
||||||
|
a = Args(c.arg_to_cmd)
|
||||||
|
a.run()
|
||||||
|
else:
|
||||||
|
stderr("The args command requires an example command as an argument. Use 'naked args help' for more information.", 1)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ build ] - build the C code in the Naked library (2)= help
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
elif c.cmd == "build":
|
||||||
|
if c.cmd2 == "help":
|
||||||
|
from Naked.commands.build import help as build_help
|
||||||
|
build_help()
|
||||||
|
else:
|
||||||
|
from Naked.commands.build import compile_c_code
|
||||||
|
import os, inspect
|
||||||
|
abs_dirpath = os.path.join(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))), "toolshed", "c")
|
||||||
|
compile_c_code(abs_dirpath) # function calls exit status code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ classify ] - search Python application classifiers and display to user (args)-search string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
elif c.cmd == "classify":
|
||||||
|
if c.cmd2 == "help":
|
||||||
|
from Naked.commands.classifier import help as classifier_help
|
||||||
|
classifier_help()
|
||||||
|
else:
|
||||||
|
if c.second: # if search string was given
|
||||||
|
search_string = c.second
|
||||||
|
else:
|
||||||
|
search_string = "" # absence of search string detected in Classifier, defaults to the entire list instead of search
|
||||||
|
from Naked.commands.classifier import Classifier
|
||||||
|
c = Classifier(search_string)
|
||||||
|
c.run()
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ dist ] - distribute source files to PyPI (2)=register, sdist, swheel, wheel, win, all, help
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
elif c.cmd == "dist":
|
||||||
|
if c.argc > 1:
|
||||||
|
from Naked.commands.dist import Dist
|
||||||
|
d = Dist()
|
||||||
|
if c.cmd2 == "register": # python setup.py register
|
||||||
|
d.run('register')
|
||||||
|
elif c.cmd2 == "sdist": # python setup.py sdist upload
|
||||||
|
d.run('sdist')
|
||||||
|
elif c.cmd2 == "swheel": # python setup.py sdist bdist_wheel upload
|
||||||
|
d.run('swheel')
|
||||||
|
elif c.cmd2 == "wheel": # python setup.py bdist_wheel upload
|
||||||
|
d.run('wheel')
|
||||||
|
elif c.cmd2 == "win": # python setup.py bdist_wininst upload
|
||||||
|
d.run('win')
|
||||||
|
elif c.cmd2 == "all": # python setup.py sdist bdist_wheel bdist_wininst upload
|
||||||
|
d.run('all')
|
||||||
|
elif c.cmd2 == "help": # help for command
|
||||||
|
from Naked.commands.dist import help as dist_help
|
||||||
|
dist_help()
|
||||||
|
else:
|
||||||
|
stderr("The naked dist secondary command was not recognized. Use 'naked dist help' for more information.", 1)
|
||||||
|
else:
|
||||||
|
stderr("Please enter a secondary command", 1)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ locate ] - locate Naked project files (2)= main, settings, setup, help
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
elif c.cmd == "locate":
|
||||||
|
from Naked.commands.locate import Locator
|
||||||
|
if c.cmd2 == "help":
|
||||||
|
from Naked.commands.locate import help as locate_help
|
||||||
|
locate_help()
|
||||||
|
elif c.cmd2 == "main":
|
||||||
|
l = Locator('main')
|
||||||
|
elif c.cmd2 == "settings":
|
||||||
|
l = Locator('settings')
|
||||||
|
elif c.cmd2 == "setup":
|
||||||
|
l = Locator('setup')
|
||||||
|
else:
|
||||||
|
l = Locator('') #handles error report to user
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ make ] - make a new Naked project (2)=help (args)=project name
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
elif c.cmd == "make":
|
||||||
|
from Naked.commands.make import MakeController
|
||||||
|
if c.cmd2 == "help":
|
||||||
|
from Naked.commands.make import help as make_help
|
||||||
|
make_help()
|
||||||
|
if c.arg1: # arg1 is not help so use it as the argument to the make command
|
||||||
|
m = MakeController(c.arg1)
|
||||||
|
else:
|
||||||
|
m = MakeController(None)
|
||||||
|
m.run()
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ profile ] - run the profiler.py file in the Naked project (2)=help
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
elif c.cmd == "profile":
|
||||||
|
if c.cmd2 == "help":
|
||||||
|
from Naked.commands.profile import help as profile_help
|
||||||
|
profile_help()
|
||||||
|
else:
|
||||||
|
from Naked.commands.profile import Profiler
|
||||||
|
p = Profiler()
|
||||||
|
p.run()
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ pyh ] - help for python built-in library modules, classes, methods, functions
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
elif c.cmd == "pyh":
|
||||||
|
if c.cmd2 == "help":
|
||||||
|
from Naked.commands.pyh import pyh_help
|
||||||
|
pyh_help()
|
||||||
|
else:
|
||||||
|
if c.argc > 1:
|
||||||
|
from Naked.commands.pyh import python_help
|
||||||
|
python_help(c.arg1)
|
||||||
|
else:
|
||||||
|
stderr("Please enter a query term with the pyh command. Use 'naked pyh help' for more information.", 1)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ test ] - Run unit tests on the project (2)= help,nose,pytest,tox,unittest (see help for args)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
elif c.cmd == "test":
|
||||||
|
if c.argc > 1:
|
||||||
|
if c.cmd2 == "help":
|
||||||
|
from Naked.commands.test import help as tox_help
|
||||||
|
tox_help()
|
||||||
|
elif c.cmd2 == "nose":
|
||||||
|
from Naked.commands.test import NoseTester
|
||||||
|
n = NoseTester()
|
||||||
|
n.run()
|
||||||
|
elif c.cmd2 == "pytest":
|
||||||
|
from Naked.commands.test import PyTester
|
||||||
|
p = PyTester()
|
||||||
|
p.run()
|
||||||
|
elif c.cmd2 == "tox":
|
||||||
|
from Naked.commands.test import ToxTester
|
||||||
|
if c.arg2: #user specified a python version to run with one of the tox version defs
|
||||||
|
t = ToxTester(c.arg2) #instantiate with the python version
|
||||||
|
else:
|
||||||
|
t = ToxTester()
|
||||||
|
t.run()
|
||||||
|
elif c.cmd2 == "unittest":
|
||||||
|
from Naked.commands.test import UnitTester
|
||||||
|
if c.arg2:
|
||||||
|
t = UnitTester(c.arg2)
|
||||||
|
t.run()
|
||||||
|
else:
|
||||||
|
stderr("Please include a unit test file path. Use 'naked test help' for more information.", 1)
|
||||||
|
else:
|
||||||
|
stderr("The secondary command was not recognized. Use 'naked test help' for more information.", 1)
|
||||||
|
else:
|
||||||
|
stderr("Please include a secondary command with the 'naked test' command. Use 'naked dist help' for more information.", 1)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ NAKED FRAMEWORK COMMANDS ]
|
||||||
|
# Naked framework provides default help, usage, and version commands for all applications
|
||||||
|
# --> settings for user messages are assigned in the lib/PROJECT/settings.py file
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
elif c.help(): # User requested naked help (help.py module in commands directory)
|
||||||
|
from Naked.commands.help import Help
|
||||||
|
Help().print_help()
|
||||||
|
elif c.usage(): # user requested naked usage info (usage.py module in commands directory)
|
||||||
|
from Naked.commands.usage import Usage
|
||||||
|
Usage().print_usage()
|
||||||
|
elif c.version(): # user requested naked version (version.py module in commands directory)
|
||||||
|
from Naked.commands.version import Version
|
||||||
|
Version().print_version()
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ DEFAULT MESSAGE FOR MATCH FAILURE ]
|
||||||
|
# Message to provide to the user when all above conditional logic fails to meet a true condition
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
else:
|
||||||
|
print("Could not complete the command that you entered. Please try again.")
|
||||||
|
sys.exit(1) #exit
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -0,0 +1,371 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
#####################################################################
|
||||||
|
# [ Command class ]
|
||||||
|
# Command line command string object
|
||||||
|
# argv = list of command line arguments and options
|
||||||
|
# argc = count of command line arguments and options
|
||||||
|
# arg0 = first positional argument to command
|
||||||
|
# arg1 = second positional argument to command
|
||||||
|
# arglp = last positional argument to command
|
||||||
|
# cmd = primary command for command suite application (=arg0)
|
||||||
|
# cmd2 = secondary command for command suite application (=arg1)
|
||||||
|
# snippet for py block comment = #py + TAB
|
||||||
|
#####################################################################
|
||||||
|
class Command:
|
||||||
|
def __init__(self, app_path, argv):
|
||||||
|
self.argobj = Argument(argv) # create an Argument obj
|
||||||
|
self.optobj = Option(argv) # create an Option obj
|
||||||
|
self.app = app_path # path to application executable file
|
||||||
|
self.argv = argv # list of the command arguments argv[0] is first argument
|
||||||
|
self.argc = len(argv) # length of the argument list
|
||||||
|
self.arg0 = self.argobj._getArg(0) # define the first positional argument
|
||||||
|
self.arg1 = self.argobj._getArg(1) # define the second positional argument
|
||||||
|
self.arg2 = self.argobj._getArg(2) # define the third postitional argument
|
||||||
|
self.arg3 = self.argobj._getArg(3) # define the fourth positional argument
|
||||||
|
self.arg4 = self.argobj._getArg(4) # define the fifth positional argument
|
||||||
|
self.arglp = self.argobj._getArg(len(argv) - 1) # define the last positional argument
|
||||||
|
self.first = self.arg0
|
||||||
|
self.second = self.arg1
|
||||||
|
self.third = self.arg2
|
||||||
|
self.fourth = self.arg3
|
||||||
|
self.fifth = self.arg4
|
||||||
|
self.last = self.arglp
|
||||||
|
self.arg_to_exec = self.arg0 # argument to the executable
|
||||||
|
self.arg_to_cmd = self.arg1 # argument to the primary command
|
||||||
|
self.cmd = self.arg0 # define the primary command variable as the first positional argument (user dependent & optional, may be something else)
|
||||||
|
self.cmd2 = self.arg1 # define the secondary command variable as the second positional argument (user dependent & optional, may be something else)
|
||||||
|
self.options = self.option_exists() # test for presence of at least one option (boolean)
|
||||||
|
self.flags = self.flag_exists() # test for presence of at least one flag (boolean)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ app_validates_args method ] (boolean)
|
||||||
|
# Test whether app validates on the criterion arguments (argc) > 0, i.e. there is at least one argument to the executable
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def app_validates_args(self):
|
||||||
|
try:
|
||||||
|
if self.argc > 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Validation of application error in the app_validates() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ arg method ] (string)
|
||||||
|
# Return the NEXT positional argument to a command line object (e.g. an option that requires an argument)
|
||||||
|
# arg_recipient = the positional argument (at position n) to test for next positional argument
|
||||||
|
# returns next positional argument string at position n + 1
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
def arg(self, arg_recipient):
|
||||||
|
try:
|
||||||
|
recipient_position = self.argobj._getArgPosition(arg_recipient)
|
||||||
|
return self.argobj._getArgNext(recipient_position)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error parsing argument with arg() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ command method ] (boolean)
|
||||||
|
# Test that the command includes requested primary command suite command (cmd_str parameter)
|
||||||
|
# cmd_str = the command string to test for in command
|
||||||
|
# arugment_required = boolean - is an argument to this command required (default = no)?
|
||||||
|
# returns boolean for presence of the cmd_str
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
def command(self, cmd_str):
|
||||||
|
try:
|
||||||
|
if (cmd_str == self.cmd):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False # if command is missing, return false
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error parsing command with command() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ command_arg method ] (string)
|
||||||
|
# Return the argument to the primary command as a string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def command_arg(self):
|
||||||
|
try:
|
||||||
|
return self.arg1
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error parsing command argument with command_arg() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ command2_arg method ] (string)
|
||||||
|
# Return the argument to the secondary command as a string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def command2_arg(self):
|
||||||
|
try:
|
||||||
|
return self.arg2
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error parsing command argument with command_arg() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ command_with_argument method ] (boolean)
|
||||||
|
# Test that the command includes requested primary command suite command (cmd_str parameter) and argument to it
|
||||||
|
# cmd_str = the command string to test for in command
|
||||||
|
# returns boolean for presence of the cmd_str AND presence of argument to the command
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
def command_with_argument(self, cmd_str):
|
||||||
|
try:
|
||||||
|
if (cmd_str == self.cmd):
|
||||||
|
argument_to_cmd = self.argobj._getArgNext(0)
|
||||||
|
if argument_to_cmd == "": # if the argument is missing, then return false
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False # if command is missing return false
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error parsing command and argument with command_with_argument() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ command_suite_validates method ] (boolean)
|
||||||
|
# Test that there is a primary command in a command suite application (to be used at the top level of logic for command line application)
|
||||||
|
# returns boolean for presence of the primary command
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
def command_suite_validates(self, accept_options_as_argument = True):
|
||||||
|
try:
|
||||||
|
if self.argc > 0:
|
||||||
|
if self.arg0.startswith("-") and accept_options_as_argument == False:
|
||||||
|
return False # if no command and option present, return False
|
||||||
|
else:
|
||||||
|
return True # if a primary command present, return True
|
||||||
|
else:
|
||||||
|
return False # if user only entered the application name, return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Command suite validation error with the command_suite_validation() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ flag method ] (boolean)
|
||||||
|
# Test for presence of flag in the command
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def flag(self, flag_string):
|
||||||
|
try:
|
||||||
|
for match_string in self.optobj: #iterate through the options and attempt to match beginning of option to the requested flag
|
||||||
|
if match_string.startswith(flag_string):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error parsing flags with the flag() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [flag_arg method] (string)
|
||||||
|
# Return the argument string assigned to a flag
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def flag_arg(self, flag_string):
|
||||||
|
try:
|
||||||
|
for match_string in self.optobj:
|
||||||
|
if match_string.startswith(flag_string) and '=' in match_string:
|
||||||
|
flag_list = match_string.split("=") #split the flag on the equal symbol = list with [option, argument]
|
||||||
|
return flag_list[1] #return the argument to the flag option
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
return "" # return an empty string if unable to parse the argument
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error parsing flags with the flag_arg() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ flag_exists method ] (boolean)
|
||||||
|
# Test for the presence of a flag style option (--flag=argument) in the command
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def flag_exists(self):
|
||||||
|
try:
|
||||||
|
for item in self.optobj:
|
||||||
|
if '=' in item: #test for presence of an = symbol in the option
|
||||||
|
return True # if present return True
|
||||||
|
break
|
||||||
|
return False # if didn't match across all options, return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error parsing flags with the flag_arg() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ option method ] (boolean)
|
||||||
|
# Test that the command includes an option (option_string parameter)
|
||||||
|
# option_string = the option string to test for in the command
|
||||||
|
# arugment_required = boolean - is an argument to this option required (default = no)?
|
||||||
|
# returns boolean for presence of the cmd_str
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
def option(self, option_string, argument_required = False):
|
||||||
|
try:
|
||||||
|
if (option_string in self.optobj):
|
||||||
|
argument_to_option = self.argobj._getArgNext(self.argobj._getArgPosition(option_string))
|
||||||
|
if argument_required and ( argument_to_option == "" or argument_to_option.startswith("-") ):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error parsing option with option() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ option_arg method ] (string)
|
||||||
|
# Return the argument string to an option
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def option_arg(self, option_string):
|
||||||
|
try:
|
||||||
|
return self.argobj._getArgNext(self.argobj._getArgPosition(option_string))
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error returning argument to option with option_arg() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ option_with_arg method ] (boolean)
|
||||||
|
# Test that the command includes an option (option_string parameter) and argument to that option
|
||||||
|
# option_string = the option string to test for in the command
|
||||||
|
# arugment_required = boolean - is an argument to this option required (default = yes)?
|
||||||
|
# returns boolean for presence of the option_string AND the argument
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# test that command includes an option (option_string parameter) that includes an argument (=option(option_string, True))
|
||||||
|
def option_with_arg(self, option_string, argument_required = True):
|
||||||
|
try:
|
||||||
|
if (option_string in self.optobj):
|
||||||
|
argument_to_option = self.argobj._getArgNext(self.argobj._getArgPosition(option_string))
|
||||||
|
if argument_required and ( argument_to_option == "" or argument_to_option.startswith("-") ):
|
||||||
|
return False # argument is either missing or is another option, return false
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False # option is not present
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error parsing option and argument with option_with_arg() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ option_exists method ] (boolean)
|
||||||
|
# Test whether there are any options in the command string
|
||||||
|
# returns boolean value for test "Is there at least one option?"
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def option_exists(self):
|
||||||
|
try:
|
||||||
|
if len(self.optobj) > 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Error testing for the presence of at least one option with option_exists() method (Naked.commandline.py).")
|
||||||
|
raise e
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Naked provides the following commands for all applications that use the framework:
|
||||||
|
# -- help
|
||||||
|
# -- usage
|
||||||
|
# -- version
|
||||||
|
# These methods are accessed from the app.py module, main() as method calls on the command line object
|
||||||
|
# Parsing logic is coded below
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Help Command/Option Handler
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def help(self):
|
||||||
|
if ( (self.option("--help")) or (self.cmd == "help") or (self.option("-h")) ):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Usage Command/Option Handler
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def usage(self):
|
||||||
|
if ( (self.option("--usage")) or (self.cmd == "usage") ):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Version Command/Option Handler
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def version(self):
|
||||||
|
if ( (self.option("--version")) or (self.cmd == "version") or (self.option("-v"))):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# print the arguments with their corresponding argv list position to std out
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def show_args(self):
|
||||||
|
x = 0
|
||||||
|
for arg in self.argv:
|
||||||
|
print("argv[" + str(x) + "] = " + arg)
|
||||||
|
x = x + 1
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ Argument Class ]
|
||||||
|
# all command line arguments (object inherited from Python list)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class Argument(list):
|
||||||
|
def __init__(self, argv):
|
||||||
|
self.argv = argv
|
||||||
|
list.__init__(self, self.argv)
|
||||||
|
|
||||||
|
|
||||||
|
# return argument at position specified by the 'position' parameter
|
||||||
|
def _getArg(self, position):
|
||||||
|
if ( self.argv ) and ( len(self.argv) > position ):
|
||||||
|
return self.argv[position]
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# return position of user specified argument in the argument list
|
||||||
|
def _getArgPosition(self, test_arg):
|
||||||
|
if ( self.argv ):
|
||||||
|
if test_arg in self.argv:
|
||||||
|
return self.argv.index(test_arg)
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
# return the argument at the next position following a user specified positional argument (e.g. for argument to an option in cmd)
|
||||||
|
def _getArgNext(self, position):
|
||||||
|
if len(self.argv) > (position + 1):
|
||||||
|
return self.argv[position + 1]
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ Option Class ]
|
||||||
|
# Command line options (object inherited from Python list)
|
||||||
|
# Definition: string that begins with "-" (i.e. can be -h or --long)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class Option(list):
|
||||||
|
def __init__(self, argv):
|
||||||
|
self.argv = argv
|
||||||
|
list.__init__(self, self._make_option_list())
|
||||||
|
|
||||||
|
# make a list of the options in the command (defined as anything that starts with "-" character)
|
||||||
|
def _make_option_list(self):
|
||||||
|
optargv = []
|
||||||
|
for x in self.argv:
|
||||||
|
if x.startswith("-"):
|
||||||
|
optargv.append(x)
|
||||||
|
return optargv
|
@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
from Naked.commandline import Command
|
||||||
|
from Naked.toolshed.system import exit_success
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
class Args:
|
||||||
|
def __init__(self, command_string):
|
||||||
|
self.com_string = command_string
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
cmd_list = shlex.split(self.com_string)
|
||||||
|
c = Command(cmd_list[0], cmd_list[1:])
|
||||||
|
print(' ')
|
||||||
|
print("•naked• Assuming that your Command object is instantiated as an instance named 'c', the command that you entered would be parsed as follows:")
|
||||||
|
print(' ')
|
||||||
|
print('Application')
|
||||||
|
print('-----------')
|
||||||
|
print('c.app = ' + c.app)
|
||||||
|
print(' ')
|
||||||
|
print('Argument List Length')
|
||||||
|
print('--------------------')
|
||||||
|
print('c.argc = ' + str(c.argc))
|
||||||
|
print(' ')
|
||||||
|
print('Argument List Items')
|
||||||
|
print('-------------------')
|
||||||
|
print('c.argobj = ' + str(c.argobj))
|
||||||
|
print(' ')
|
||||||
|
print('Arguments by Zero Indexed Start Position')
|
||||||
|
print('----------------------------------------')
|
||||||
|
print('c.arg0 = ' + c.arg0)
|
||||||
|
print('c.arg1 = ' + c.arg1)
|
||||||
|
print('c.arg2 = ' + c.arg2)
|
||||||
|
print('c.arg3 = ' + c.arg3)
|
||||||
|
print('c.arg4 = ' + c.arg4)
|
||||||
|
print(' ')
|
||||||
|
print('Arguments by Named Position')
|
||||||
|
print('---------------------------')
|
||||||
|
print('c.first = ' + c.first)
|
||||||
|
print('c.second = ' + c.second)
|
||||||
|
print('c.third = ' + c.third)
|
||||||
|
print('c.fourth = ' + c.fourth)
|
||||||
|
print('c.fifth = ' + c.fifth)
|
||||||
|
print(' ')
|
||||||
|
print('Last Positional Argument')
|
||||||
|
print('------------------------')
|
||||||
|
print('c.arglp = ' + c.arglp)
|
||||||
|
print('c.last = ' + c.last)
|
||||||
|
print(' ')
|
||||||
|
print('Primary & Secondary Commands')
|
||||||
|
print('----------------------------')
|
||||||
|
print('c.cmd = ' + c.cmd)
|
||||||
|
print('c.cmd2 = ' + c.cmd2)
|
||||||
|
print(' ')
|
||||||
|
print('Option Exists Tests')
|
||||||
|
print('------------------')
|
||||||
|
print('c.option_exists() = ' + str(c.option_exists()))
|
||||||
|
print('c.options = ' + str(c.options))
|
||||||
|
print(' ')
|
||||||
|
print('Option Argument Assignment')
|
||||||
|
print('--------------------------')
|
||||||
|
if c.option_exists(): # if there are options, iterate through and print arguments to them
|
||||||
|
non_flag_options = False
|
||||||
|
for x in range(len(c.optobj)):
|
||||||
|
if '=' in c.optobj[x]:
|
||||||
|
continue # don't print flags, they are handled below
|
||||||
|
else:
|
||||||
|
print('c.arg("' + c.optobj[x] + '") = ' + c.arg(c.optobj[x]))
|
||||||
|
non_flag_options = True
|
||||||
|
if not non_flag_options:
|
||||||
|
print("There are no short or long options in the command.")
|
||||||
|
else: # otherwise, inform user that there are no options
|
||||||
|
print("There are no short options, long options, or flags in your command.")
|
||||||
|
print(' ')
|
||||||
|
print('Flag Exists Tests')
|
||||||
|
print('----------------')
|
||||||
|
print('c.flag_exists() = ' + str(c.flag_exists()))
|
||||||
|
print('c.flags = ' + str(c.flags))
|
||||||
|
print(' ')
|
||||||
|
print('Flag Argument Assignment')
|
||||||
|
print('------------------------')
|
||||||
|
if c.flag_exists():
|
||||||
|
for y in c.optobj:
|
||||||
|
if '=' in y:
|
||||||
|
the_flag = y.split('=')[0]
|
||||||
|
print('c.flag_arg("' + the_flag + '") = ' + c.flag_arg(the_flag))
|
||||||
|
else: # provide message if there are no flags
|
||||||
|
print("There are no flag style arguments (--flag=argument) in your command.")
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ help function ] - help for the where command
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def help():
|
||||||
|
from Naked.toolshed.system import exit_success
|
||||||
|
help_string = """
|
||||||
|
Naked args Command Help
|
||||||
|
=======================
|
||||||
|
The args command displays information about the data that are parsed from a command string to Command object attributes and that are obtained from Command object methods. It is intended to help with the design of your application logic when you use the Naked command line parser.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
naked args '<command statement>'
|
||||||
|
|
||||||
|
The command statement is a mandatory argument to the command. It should include a complete command as it would be entered on the command line, including the executable. The argument should be completely enclosed within quotes.
|
||||||
|
|
||||||
|
SECONDARY COMMANDS
|
||||||
|
none
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
none
|
||||||
|
|
||||||
|
EXAMPLE
|
||||||
|
naked args 'testapp save somestring --unicode -s --name=file.txt'"""
|
||||||
|
print(help_string)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
from Naked.toolshed.system import exit_success
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ compile_c_code function ] (--none--)
|
||||||
|
# compile C files in the lib/Naked/toolshed/c directory
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def compile_c_code(abs_dirpath):
|
||||||
|
from Naked.toolshed.shell import execute
|
||||||
|
from os import chdir
|
||||||
|
|
||||||
|
chdir(abs_dirpath)
|
||||||
|
print('•naked• Compiling the C source library files...')
|
||||||
|
success = execute("python setup.py build_ext --inplace")
|
||||||
|
if success:
|
||||||
|
print(' ')
|
||||||
|
print('•naked• C source code compile complete.')
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
def help():
|
||||||
|
help_string = """
|
||||||
|
Naked build Command Help
|
||||||
|
========================
|
||||||
|
The build command compiles the Naked C libraries. This requires an installed C compiler.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
naked build
|
||||||
|
|
||||||
|
SECONDARY COMMANDS
|
||||||
|
none
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
none"""
|
||||||
|
print(help_string)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
from Naked.toolshed.system import exit_success
|
||||||
|
|
||||||
|
class Classifier:
|
||||||
|
def __init__(self, search_string):
|
||||||
|
self.needle = search_string
|
||||||
|
self.url = 'https://pypi.python.org/pypi?%3Aaction=list_classifiers'
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
from Naked.toolshed.network import HTTP
|
||||||
|
http = HTTP(self.url) # use the python.org url for the classifier list
|
||||||
|
|
||||||
|
print('•naked• Pulling the classifier list from python.org...')
|
||||||
|
|
||||||
|
res = http.get() # get the list
|
||||||
|
test_list = res.split('\n') # split on newlines
|
||||||
|
|
||||||
|
if self.needle == "": # user did not enter a search string, print the entire list
|
||||||
|
print("•naked• You did not provide a search string. Here is the entire list:")
|
||||||
|
print(' ')
|
||||||
|
for item in test_list:
|
||||||
|
print(item)
|
||||||
|
else: # user entered a search string, find it
|
||||||
|
lower_needle = self.needle.lower()
|
||||||
|
print("•naked• Performing a case insensitive search for '" + self.needle + "'")
|
||||||
|
print(' ')
|
||||||
|
filtered_list = [ x for x in test_list if lower_needle in x.lower() ] #case insensitive match for the search string
|
||||||
|
for item in filtered_list:
|
||||||
|
print(item)
|
||||||
|
|
||||||
|
exit_success() # exit with zero status code
|
||||||
|
|
||||||
|
def help():
|
||||||
|
help_string = """
|
||||||
|
Naked classify Command Help
|
||||||
|
===========================
|
||||||
|
The classify command performs a case-insensitive search of the PyPI application classifier list and displays the results.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
naked classify [search string]
|
||||||
|
|
||||||
|
The search string argument is optional. If you do not include a search string, the entire classifier list is displayed.
|
||||||
|
|
||||||
|
SECONDARY COMMANDS
|
||||||
|
none
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
none
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
naked classify
|
||||||
|
naked classify Internet
|
||||||
|
"""
|
||||||
|
print(help_string)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import os
|
||||||
|
from Naked.toolshed.system import file_exists, stderr, exit_success
|
||||||
|
from Naked.toolshed.shell import run as shell_run
|
||||||
|
|
||||||
|
class Dist:
|
||||||
|
def __init__(self):
|
||||||
|
self.register = "python setup.py register"
|
||||||
|
self.sdist = "python setup.py sdist upload"
|
||||||
|
self.wheel = "python setup.py bdist_wheel upload"
|
||||||
|
self.swheel = "python setup.py sdist bdist_wheel upload"
|
||||||
|
self.win = "python setup.py bdist_wininst upload"
|
||||||
|
self.all = "python setup.py sdist bdist_wheel bdist_wininst upload"
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ run method ] - iterates through up to 6 directories above current working
|
||||||
|
# directory and then runs command if setup.py found
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def run(self, command):
|
||||||
|
setuppy_found = False
|
||||||
|
for i in range(6): # navigate up at most 4 directory levels to search for the setup.py file
|
||||||
|
if not self._is_setup_py_at_this_level():
|
||||||
|
os.chdir(os.pardir)
|
||||||
|
else:
|
||||||
|
setuppy_found = True
|
||||||
|
self._run_dist_command(command)
|
||||||
|
break
|
||||||
|
if not setuppy_found:
|
||||||
|
stderr("Unable to locate the setup.py file for your project. Please confirm that you are in your project directory and try again.", 1)
|
||||||
|
else:
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
# search for setup.py file
|
||||||
|
def _is_setup_py_at_this_level(self):
|
||||||
|
if file_exists('setup.py'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# run the user requested command
|
||||||
|
def _run_dist_command(self, the_command):
|
||||||
|
if the_command in "register":
|
||||||
|
print('•naked• Running register...')
|
||||||
|
shell_run(self.register)
|
||||||
|
elif the_command in "sdist":
|
||||||
|
print('•naked• Running sdist...')
|
||||||
|
shell_run(self.sdist)
|
||||||
|
elif the_command in "wheel":
|
||||||
|
print('•naked• Running wheel...')
|
||||||
|
shell_run(self.wheel)
|
||||||
|
elif the_command in "swheel":
|
||||||
|
print('•naked• Running swheel...')
|
||||||
|
shell_run(self.swheel)
|
||||||
|
elif the_command in "win":
|
||||||
|
print('•naked• Running win...')
|
||||||
|
shell_run(self.win)
|
||||||
|
elif the_command in "all":
|
||||||
|
print('•naked• Running all...')
|
||||||
|
shell_run(self.all)
|
||||||
|
else:
|
||||||
|
stderr("Unrecognized command. Use 'naked dist help' to view the supported commands.", 1)
|
||||||
|
|
||||||
|
|
||||||
|
def help():
|
||||||
|
help_string = """
|
||||||
|
Naked dist Command Help
|
||||||
|
=======================
|
||||||
|
The dist secondary commands run the standard distutils 'python setup.py <command>' source/binary distribution commands.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
naked dist <secondary_command>
|
||||||
|
|
||||||
|
SECONDARY COMMANDS python setup.py <command(s)>
|
||||||
|
all sdist bdist_wheel bdist_wininst upload
|
||||||
|
register register
|
||||||
|
sdist sdist upload
|
||||||
|
swheel sdist bdist_wheel upload
|
||||||
|
wheel bdist_wheel upload
|
||||||
|
win bdist_wininst upload
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
none
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
naked dist register
|
||||||
|
naked dist sdist"""
|
||||||
|
print(help_string)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import Naked.settings
|
||||||
|
from Naked.toolshed.system import exit_success
|
||||||
|
|
||||||
|
class Help:
|
||||||
|
def __init__(self):
|
||||||
|
self.help = Naked.settings.help
|
||||||
|
|
||||||
|
def print_help(self):
|
||||||
|
print(self.help)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import os
|
||||||
|
from Naked.toolshed.system import stderr, exit_success
|
||||||
|
|
||||||
|
class Locator:
|
||||||
|
def __init__(self, needle):
|
||||||
|
self.needle = needle
|
||||||
|
self.location = self._display_location()
|
||||||
|
|
||||||
|
def _display_location(self):
|
||||||
|
if self.needle == 'main':
|
||||||
|
main_path = os.path.join('<PROJECT>', 'lib', '<PROJECT>', 'app.py')
|
||||||
|
print("app.py : " + main_path)
|
||||||
|
exit_success()
|
||||||
|
elif self.needle == "settings":
|
||||||
|
settings_path = os.path.join('<PROJECT>', 'lib', '<PROJECT>','settings.py')
|
||||||
|
print("settings.py : " + settings_path)
|
||||||
|
exit_success()
|
||||||
|
elif self.needle == "setup":
|
||||||
|
setup_path = os.path.join('<PROJECT>', 'setup.py')
|
||||||
|
print("setup.py : " + setup_path)
|
||||||
|
exit_success()
|
||||||
|
else:
|
||||||
|
stderr("Unable to process the command. Use 'naked locate help' for more information.", 1)
|
||||||
|
|
||||||
|
def help():
|
||||||
|
help_string = """
|
||||||
|
Naked locate Command Help
|
||||||
|
=========================
|
||||||
|
The locate command identifies the file path to commonly used files in your project directory.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
naked locate <argument>
|
||||||
|
|
||||||
|
SECONDARY COMMANDS
|
||||||
|
main - the main application script file, app.py
|
||||||
|
setup - the setup.py file
|
||||||
|
settings - the project settings files, settings.py
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
none
|
||||||
|
|
||||||
|
EXAMPLE
|
||||||
|
naked locate main"""
|
||||||
|
print(help_string)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,388 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import Naked.toolshed.system as system
|
||||||
|
import Naked.toolshed.python as python
|
||||||
|
import Naked.toolshed.file as nfile
|
||||||
|
import Naked.toolshed.ink as ink
|
||||||
|
from Naked.toolshed.types import XDict, XString
|
||||||
|
from Naked.toolshed.system import make_dirs, make_path, exit_success
|
||||||
|
import datetime
|
||||||
|
import sys
|
||||||
|
|
||||||
|
## TODO: Check for a local settings file (appname.yaml)
|
||||||
|
## TODO: make directories and files
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ MakeController class ]
|
||||||
|
# Top level logic for the make command
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class MakeController:
|
||||||
|
def __init__(self, app_name):
|
||||||
|
self.app_name = app_name
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if self.app_name == None:
|
||||||
|
i = InfoCompiler(None)
|
||||||
|
data_container = i.getSetupFileInfo()
|
||||||
|
else:
|
||||||
|
i = InfoCompiler(self.app_name)
|
||||||
|
data_container = i.getUserInfo()
|
||||||
|
|
||||||
|
db = DirectoryBuilder(data_container)
|
||||||
|
db.build()
|
||||||
|
fb = FileBuilder(data_container)
|
||||||
|
if fb.build_and_write(): # file writes were successful
|
||||||
|
main_script_path = make_path(data_container.app_name, 'lib', data_container.app_name, 'app.py')
|
||||||
|
settings_path = make_path(data_container.app_name, 'lib', data_container.app_name, 'settings.py')
|
||||||
|
command_dir = make_path(data_container.app_name, 'lib', data_container.app_name, 'commands')
|
||||||
|
setuppy_path = make_path(data_container.app_name, 'setup.py')
|
||||||
|
print(" ")
|
||||||
|
print(data_container.app_name + " was successfully built.")
|
||||||
|
print(" ")
|
||||||
|
print("-----")
|
||||||
|
print("Main application script: " + main_script_path)
|
||||||
|
print("Settings file: " + settings_path)
|
||||||
|
print("Commands directory: " + command_dir)
|
||||||
|
print("setup.py file: " + setuppy_path)
|
||||||
|
print("-----")
|
||||||
|
print(" ")
|
||||||
|
print("Use 'python setup.py develop' from the top level of your project and you can begin testing your application with the executable, " + data_container.app_name)
|
||||||
|
exit_success()
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ InfoCompiler class ]
|
||||||
|
# obtain information from user in order to build a new project
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class InfoCompiler:
|
||||||
|
def __init__(self, app_name):
|
||||||
|
self.data = DataContainer()
|
||||||
|
self.data.app_name = app_name
|
||||||
|
self.displayed_info_flag = 0
|
||||||
|
|
||||||
|
def getUserInfo(self):
|
||||||
|
if not self.displayed_info_flag:
|
||||||
|
print("We need some information to create your project.")
|
||||||
|
self.displayed_info_flag = 1
|
||||||
|
# If no project name, then query for it because this is mandatory
|
||||||
|
if self.data.app_name == None:
|
||||||
|
if python.is_py2:
|
||||||
|
response = raw_input("Please enter your application name (q=quit): ")
|
||||||
|
else:
|
||||||
|
response = input("Please enter your application name (q=quit): ")
|
||||||
|
if len(response) > 0:
|
||||||
|
if response == "q":
|
||||||
|
print("Aborted project build.")
|
||||||
|
sys.exit(0) # user requested quit
|
||||||
|
else:
|
||||||
|
if len(response.split()) > 1: # if more than one word
|
||||||
|
print("The application name must be a single word. Please try again.")
|
||||||
|
self.getUserInfo()
|
||||||
|
else:
|
||||||
|
self.data.app_name = response
|
||||||
|
else:
|
||||||
|
print("The Naked project will not build without an application name. Please try again.")
|
||||||
|
return self.getUserInfo()
|
||||||
|
# if project name already set, then obtain the other optional information
|
||||||
|
if python.is_py2():
|
||||||
|
self.data.developer = raw_input("Enter the licensing developer or organization (q=quit): ")
|
||||||
|
if self.data.developer == "q":
|
||||||
|
print("Aborted the project build.")
|
||||||
|
sys.exit(0)
|
||||||
|
self.data.license = raw_input("Enter the license type (or leave blank, q=quit): ")
|
||||||
|
if self.data.license == "q":
|
||||||
|
print("Aborted the project build.")
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
self.data.developer = input("Enter the licensing developer or organization: ")
|
||||||
|
if self.data.developer == "q":
|
||||||
|
print("Aborted the project build.")
|
||||||
|
sys.exit(0)
|
||||||
|
self.data.license = input("Enter the license type (or leave blank): ")
|
||||||
|
if self.data.license == "q":
|
||||||
|
print("Aborted the project build.")
|
||||||
|
sys.exit(0)
|
||||||
|
if self.confirmData():
|
||||||
|
return self.data
|
||||||
|
else:
|
||||||
|
print("Let's try again...")
|
||||||
|
return self.getUserInfo() # try again
|
||||||
|
|
||||||
|
def getSetupFileInfo(self):
|
||||||
|
files = system.list_all_files_cwd()
|
||||||
|
if len(files) > 0:
|
||||||
|
setupfile_exists = False
|
||||||
|
for a_file in files:
|
||||||
|
if 'naked.yaml' == a_file.lower(): # accepts any permutation of upper/lower case 'naked.yaml'
|
||||||
|
print("Detected a Naked project YAML setup file (" + a_file + ").")
|
||||||
|
setupfile_exists = True
|
||||||
|
fr = nfile.FileReader(a_file)
|
||||||
|
the_yaml = fr.read_utf8()
|
||||||
|
self.parseYaml(the_yaml)
|
||||||
|
if setupfile_exists:
|
||||||
|
if self.confirmData():
|
||||||
|
return self.data
|
||||||
|
else:
|
||||||
|
print("Aborted the project build.")
|
||||||
|
if python.is_py2():
|
||||||
|
response = raw_input("Would you like to modify this information? (y/n) ")
|
||||||
|
else:
|
||||||
|
response = input("Would you like to modify this information? (y/n) ")
|
||||||
|
if response in ['y', 'Y', 'Yes', 'YES', 'yes']:
|
||||||
|
self.displayed_info_flag = 1
|
||||||
|
self.data.app_name = None
|
||||||
|
return self.getUserInfo() # return the result from the getUserInfo command to the calling method
|
||||||
|
else:
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
return self.getUserInfo() # there are files but no setup file, use the manual entry method
|
||||||
|
else:
|
||||||
|
return self.getUserInfo() # there are no files in the directory, use the manual entry method
|
||||||
|
|
||||||
|
|
||||||
|
def parseYaml(self, yaml_string):
|
||||||
|
import yaml
|
||||||
|
the_yaml = yaml.load(yaml_string)
|
||||||
|
# Parse project name
|
||||||
|
if 'application' in the_yaml:
|
||||||
|
self.data.app_name = the_yaml['application']
|
||||||
|
else:
|
||||||
|
print("Unable to find the application name ('application' field) in naked.yaml")
|
||||||
|
if python.is_py2:
|
||||||
|
response = raw_input("Please enter your application name: ")
|
||||||
|
else:
|
||||||
|
response = input("Please enter your application name: ")
|
||||||
|
if len(response) > 0:
|
||||||
|
self.data.app_name = response # assign the application name at CL if was not entered in file
|
||||||
|
else:
|
||||||
|
print("The Naked project will not build without an application name. Please try again.")
|
||||||
|
self.displayed_info_flag = 1
|
||||||
|
self.getUserInfo()
|
||||||
|
# Parse developer
|
||||||
|
if 'developer' in the_yaml:
|
||||||
|
self.data.developer = the_yaml['developer'] # set developer
|
||||||
|
else:
|
||||||
|
self.data.developer = ""
|
||||||
|
# Parse license type
|
||||||
|
if 'license' in the_yaml:
|
||||||
|
self.data.license = the_yaml['license'] # set license
|
||||||
|
else:
|
||||||
|
self.data.license = ""
|
||||||
|
|
||||||
|
|
||||||
|
def confirmData(self):
|
||||||
|
templ_str = getHeaderTemplate()
|
||||||
|
template = ink.Template(templ_str)
|
||||||
|
renderer = ink.Renderer(template, {'app_name': self.data.app_name, 'developer': self.data.developer, 'license': self.data.license, 'year': self.data.year})
|
||||||
|
display_header = renderer.render()
|
||||||
|
print("\nPlease confirm the information below:")
|
||||||
|
print(display_header)
|
||||||
|
|
||||||
|
if python.is_py2():
|
||||||
|
response = raw_input("Is this correct? (y/n) ")
|
||||||
|
else:
|
||||||
|
response = input("Is this correct? (y/n) ")
|
||||||
|
|
||||||
|
if response in ['y', 'Y', 'yes', 'YES']:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
self.data.app_name = None
|
||||||
|
return False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ getHeaderTemplate function ] (string)
|
||||||
|
# returns the Ink header template for user confirmation
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def getHeaderTemplate():
|
||||||
|
templ_str = """
|
||||||
|
----------------------------------------------------------
|
||||||
|
{{app_name}}
|
||||||
|
Copyright {{year}} {{developer}}
|
||||||
|
{{license}}
|
||||||
|
----------------------------------------------------------
|
||||||
|
"""
|
||||||
|
return templ_str
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ DataContainer class ]
|
||||||
|
# state maintenance object that holds project information
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class DataContainer:
|
||||||
|
def __init__(self):
|
||||||
|
self.cwd = system.cwd()
|
||||||
|
self.year = str(datetime.datetime.now().year)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ DirectoryBuilder class ]
|
||||||
|
# generation of directory structure for a new project
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class DirectoryBuilder:
|
||||||
|
def __init__(self, data_container):
|
||||||
|
self.data_container = data_container
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
top_level_dir = self.data_container.app_name
|
||||||
|
second_level_dirs = ['docs', 'lib', 'tests']
|
||||||
|
lib_subdir = make_path(self.data_container.app_name, 'commands')
|
||||||
|
|
||||||
|
for xdir in second_level_dirs:
|
||||||
|
make_dirs(make_path(top_level_dir, xdir))
|
||||||
|
|
||||||
|
make_dirs(make_path(top_level_dir, 'lib', lib_subdir))
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ FileBuilder class ]
|
||||||
|
# generate the files for a new project
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class FileBuilder:
|
||||||
|
def __init__(self, data_container):
|
||||||
|
self.data_container = data_container
|
||||||
|
self.file_dictionary = {}
|
||||||
|
|
||||||
|
def build_and_write(self):
|
||||||
|
self._make_file_paths() # create the file paths for all generated files
|
||||||
|
self._render_file_strings() # create the rendered template strings
|
||||||
|
self._make_file_dictionary() # make the file path : file string dictionary
|
||||||
|
self.write_files() # write out to files
|
||||||
|
return True # if made it this far without exception, return True to calling method to confirm file writes
|
||||||
|
|
||||||
|
# files are included in self.file_dictionary as key = filepath, value = filestring pairs
|
||||||
|
# write the files to disk
|
||||||
|
def write_files(self):
|
||||||
|
the_file_xdict = XDict(self.file_dictionary)
|
||||||
|
for filepath, file_string in the_file_xdict.xitems():
|
||||||
|
fw = nfile.FileWriter(filepath)
|
||||||
|
try:
|
||||||
|
fw.write_utf8(file_string)
|
||||||
|
except TypeError as te: # catch unicode write errors
|
||||||
|
fw.write(file_string)
|
||||||
|
|
||||||
|
def _make_file_paths(self):
|
||||||
|
from Naked.toolshed.system import make_path
|
||||||
|
|
||||||
|
self.top_manifestin = make_path(self.data_container.app_name, 'MANIFEST.in')
|
||||||
|
self.top_readmemd = make_path(self.data_container.app_name, 'README.md')
|
||||||
|
self.top_setupcfg = make_path(self.data_container.app_name, 'setup.cfg')
|
||||||
|
self.top_setuppy = make_path(self.data_container.app_name, 'setup.py')
|
||||||
|
self.docs_license = make_path(self.data_container.app_name, 'docs', 'LICENSE')
|
||||||
|
self.docs_readmerst = make_path(self.data_container.app_name, 'docs', 'README.rst')
|
||||||
|
self.lib_initpy = make_path(self.data_container.app_name, 'lib', '__init__.py')
|
||||||
|
self.com_initpy = make_path(self.data_container.app_name, 'lib', self.data_container.app_name, 'commands', '__init__.py')
|
||||||
|
self.tests_initpy = make_path(self.data_container.app_name, 'tests', '__init__.py')
|
||||||
|
self.lib_profilerpy = make_path(self.data_container.app_name, 'lib', 'profiler.py')
|
||||||
|
self.lib_proj_initpy = make_path(self.data_container.app_name, 'lib', self.data_container.app_name, '__init__.py')
|
||||||
|
self.lib_proj_apppy = make_path(self.data_container.app_name, 'lib', self.data_container.app_name, 'app.py')
|
||||||
|
self.lib_proj_settingspy = make_path(self.data_container.app_name, 'lib', self.data_container.app_name, 'settings.py')
|
||||||
|
|
||||||
|
def _render_file_strings(self):
|
||||||
|
from Naked.templates.manifest_in_file import manifest_file_string
|
||||||
|
from Naked.templates.readme_md_file import readme_md_string
|
||||||
|
from Naked.templates.setup_cfg_file import setup_cfg_string
|
||||||
|
from Naked.templates.setup_py_file import setup_py_string
|
||||||
|
from Naked.templates.profiler_file import profiler_file_string
|
||||||
|
from Naked.templates.app_file import app_file_string
|
||||||
|
from Naked.templates.settings_file import settings_file_string
|
||||||
|
|
||||||
|
data_dict = self.data_container.__dict__
|
||||||
|
|
||||||
|
self.top_manifestin_rendered = manifest_file_string # no replacements necessary
|
||||||
|
self.top_readmemd_rendered = self._render_template(self._create_template(readme_md_string), data_dict) #requires app_name replacement
|
||||||
|
self.top_setupcfg_rendered = setup_cfg_string # no replacement necessary
|
||||||
|
self.top_setuppy_rendered = self._render_template(self._create_template(setup_py_string), data_dict) # requires app_name, developer replacements
|
||||||
|
self.docs_readmerst_rendered = "" # blank document stub write
|
||||||
|
self.lib_profilerpy_rendered = profiler_file_string # no replacement necessary
|
||||||
|
self.initpy_rendered = "" # blank __init__.py files
|
||||||
|
self.lib_proj_apppy_rendered = self._render_template(self._create_template(app_file_string), data_dict) # requires app_name, developer, license_name, year replacements
|
||||||
|
self.lib_proj_settingspy_rendered = self._render_template(self._create_template(settings_file_string), data_dict) # requires app_name replacement
|
||||||
|
|
||||||
|
if len(self.data_container.license) > 0:
|
||||||
|
license = self.parse_licenses(self.data_container.license) # find the appropriate license template if the license was provided by user
|
||||||
|
if len(license) > 0: # could be empty string if fails to match a license template provided by Naked
|
||||||
|
self.docs_license_rendered = self._render_template(self._create_template(license), data_dict)
|
||||||
|
else:
|
||||||
|
self.docs_license_rendered = ""
|
||||||
|
|
||||||
|
def _make_file_dictionary(self):
|
||||||
|
file_dictionary = {}
|
||||||
|
## File path : file string key/value pairs > make as XString and encode as unicode for unicode file writes
|
||||||
|
file_dictionary[self.top_manifestin] = XString(self.top_manifestin_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.top_readmemd] = XString(self.top_readmemd_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.top_setupcfg] = XString(self.top_setupcfg_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.top_setuppy] = XString(self.top_setuppy_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.docs_license] = XString(self.docs_license_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.docs_readmerst] = XString(self.docs_readmerst_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.lib_initpy] = XString(self.initpy_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.com_initpy] = XString(self.initpy_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.tests_initpy] = XString(self.initpy_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.lib_profilerpy] = XString(self.lib_profilerpy_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.lib_proj_initpy] = XString(self.initpy_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.lib_proj_apppy] = XString(self.lib_proj_apppy_rendered).unicode().strip()
|
||||||
|
file_dictionary[self.lib_proj_settingspy] = XString(self.lib_proj_settingspy_rendered).unicode().strip()
|
||||||
|
|
||||||
|
self.file_dictionary = file_dictionary
|
||||||
|
|
||||||
|
def _create_template(self, template_string):
|
||||||
|
return ink.Template(template_string)
|
||||||
|
|
||||||
|
def _render_template(self, template, key_dict):
|
||||||
|
r = ink.Renderer(template, key_dict)
|
||||||
|
return r.render()
|
||||||
|
|
||||||
|
def parse_licenses(self, license_string):
|
||||||
|
if len(license_string) > 0:
|
||||||
|
license = license_string.lower() # case insensitive matching, make lower case version
|
||||||
|
|
||||||
|
if license.startswith('apache'):
|
||||||
|
from Naked.templates.licenses import apache_license
|
||||||
|
return apache_license
|
||||||
|
elif license.startswith('bsd'):
|
||||||
|
from Naked.templates.licenses import bsd_license
|
||||||
|
return bsd_license
|
||||||
|
elif license.startswith('gpl'):
|
||||||
|
from Naked.templates.licenses import gpl3_license
|
||||||
|
return gpl3_license
|
||||||
|
elif license.startswith('lgpl'):
|
||||||
|
from Naked.templates.licenses import lgpl_license
|
||||||
|
return lgpl_license
|
||||||
|
elif license.startswith('mit'):
|
||||||
|
from Naked.templates.licenses import mit_license
|
||||||
|
return mit_license
|
||||||
|
elif license.startswith('mozilla'):
|
||||||
|
from Naked.templates.licenses import mozilla_license
|
||||||
|
return mozilla_license
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def help():
|
||||||
|
from Naked.toolshed.system import exit_success
|
||||||
|
help_string = """
|
||||||
|
Naked make Command Help
|
||||||
|
=======================
|
||||||
|
The make command builds a new Naked project. The project can be built from either responses that you give on the command line, or from a naked.yaml project settings file.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
naked make [argument]
|
||||||
|
|
||||||
|
The command should be run in the top level of the path where you would like to create your project. The argument to the make command is optional. If used, this is the name of your new project. It is not necessary to include the argument if you use a naked.yaml project settings file.
|
||||||
|
|
||||||
|
The naked.yaml settings file has the following structure:
|
||||||
|
|
||||||
|
application: <your project name>
|
||||||
|
developer: <developer name>
|
||||||
|
license: <license type>
|
||||||
|
|
||||||
|
Place this in the top level of an empty directory and use `naked make` in the same directory. Naked will confirm your settings and then build the project directories and files from these settings.
|
||||||
|
|
||||||
|
SECONDARY COMMANDS
|
||||||
|
none
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
none
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
naked make
|
||||||
|
naked make testapp"""
|
||||||
|
print(help_string)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import os
|
||||||
|
from Naked.toolshed.system import file_exists, dir_exists, stderr, exit_success
|
||||||
|
|
||||||
|
class Profiler:
|
||||||
|
def __init__(self, dir_levels = 6):
|
||||||
|
self.number_of_dir_levels = dir_levels # number of directory levels to bottom to top search
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
lib_found = False
|
||||||
|
for i in range(self.number_of_dir_levels):
|
||||||
|
if not self._is_lib_at_this_level():
|
||||||
|
os.chdir(os.pardir)
|
||||||
|
else:
|
||||||
|
lib_found = True
|
||||||
|
break
|
||||||
|
if lib_found:
|
||||||
|
os.chdir('lib') # chdir to the lib directory if it is found
|
||||||
|
if file_exists('profiler.py'): # confirm that profiler.py exists
|
||||||
|
os.system('python profiler.py') # run the profiler.py file
|
||||||
|
exit_success()
|
||||||
|
else:
|
||||||
|
stderr("Unable to locate a profiler.py file in your lib directory.", 1)
|
||||||
|
else:
|
||||||
|
stderr("Unable to locate your profiler.py file. Please navigate to your project directory.", 1)
|
||||||
|
|
||||||
|
def _is_lib_at_this_level(self):
|
||||||
|
if dir_exists('lib'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def help():
|
||||||
|
from Naked.toolshed.system import exit_success
|
||||||
|
help_string = """
|
||||||
|
Naked profile Command Help
|
||||||
|
==========================
|
||||||
|
The profile command runs cProfile and pstats on the code that you enter in test code block of your PROJECT/lib/profiler.py file.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
naked profile
|
||||||
|
|
||||||
|
SECONDARY COMMANDS
|
||||||
|
none
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
none
|
||||||
|
|
||||||
|
This command searches bottom to top (from the working directory) through up to 6 directory levels to identify the lib/profiler.py path."""
|
||||||
|
|
||||||
|
print(help_string)
|
||||||
|
exit_success()
|
||||||
|
|
@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def python_help(help_string):
|
||||||
|
try:
|
||||||
|
needle = help_string
|
||||||
|
if needle.startswith("'") and needle.endswith("'"):
|
||||||
|
needle = needle[1:-1]
|
||||||
|
elif needle.startswith('"') and needle.endswith('"'):
|
||||||
|
needle = needle[1:-1]
|
||||||
|
help(needle)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
sys.stderr.write("•naked• There was an error processing the query.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def pyh_help():
|
||||||
|
from Naked.toolshed.system import exit_success
|
||||||
|
help_string = """
|
||||||
|
Naked pyh Command Help
|
||||||
|
======================
|
||||||
|
The pyh command searches the built-in Python documentation for a query term. The query term can be a Python built-in module, class/type, method, or function.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
naked pyh <query>
|
||||||
|
|
||||||
|
SECONDARY COMMANDS
|
||||||
|
none
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
none
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
Module Docs: naked pyh sys
|
||||||
|
|
||||||
|
Class Docs: naked pyh dict
|
||||||
|
|
||||||
|
Method Docs: naked pyh dict.update
|
||||||
|
|
||||||
|
Function Docs: naked pyh max"""
|
||||||
|
|
||||||
|
print(help_string)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,204 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import os
|
||||||
|
from Naked.toolshed.system import cwd, file_exists, dir_exists, stderr, exit_success
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ ToxTester class ]
|
||||||
|
# Run Tox on the project directory, by default runs all python versions in tox.ini
|
||||||
|
# Optional specify the version of Python to test in constructor (runs 'tox -e py<version>')
|
||||||
|
# Optional specify the number of directory levels `dir_levels` to search bottom to top (default = 4)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class ToxTester:
|
||||||
|
def __init__(self, py_version="", dir_levels = 6):
|
||||||
|
self.py_version = py_version
|
||||||
|
self.number_of_dir_levels = dir_levels
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
tox_found = False
|
||||||
|
for i in range(self.number_of_dir_levels):
|
||||||
|
if not self._is_tox_ini_at_this_level():
|
||||||
|
os.chdir(os.pardir)
|
||||||
|
else:
|
||||||
|
tox_found = True
|
||||||
|
self._run_tox()
|
||||||
|
break
|
||||||
|
if not tox_found:
|
||||||
|
stderr("Unable to locate your tox.ini file. Please navigate to your project directory.", 1)
|
||||||
|
else:
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
def _is_tox_ini_at_this_level(self):
|
||||||
|
if file_exists('tox.ini'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _run_tox(self):
|
||||||
|
if self.py_version == "":
|
||||||
|
os.system("tox")
|
||||||
|
else:
|
||||||
|
cmd_string = "tox -e" + self.py_version
|
||||||
|
os.system(cmd_string)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ NoseTester class ]
|
||||||
|
# run nose tests from the tests directory (works from any level of the project)
|
||||||
|
# Optional specify the number of directory levels to search bottom to top (default = 4)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class NoseTester:
|
||||||
|
def __init__(self, dir_levels = 6):
|
||||||
|
self.number_of_dir_levels = dir_levels
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
nose_found = False
|
||||||
|
for i in range(self.number_of_dir_levels):
|
||||||
|
if not self._is_testdir_at_this_level():
|
||||||
|
os.chdir(os.pardir)
|
||||||
|
else:
|
||||||
|
nose_found = True
|
||||||
|
self._run_nose()
|
||||||
|
break
|
||||||
|
if not nose_found:
|
||||||
|
stderr("Unable to locate your testing directory", 1)
|
||||||
|
else:
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
def _is_testdir_at_this_level(self):
|
||||||
|
if file_exists('setup.py'):
|
||||||
|
if dir_exists('tests'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False #found setup.py but no tests directory
|
||||||
|
else:
|
||||||
|
return False # setup.py not at this level
|
||||||
|
|
||||||
|
def _run_nose(self):
|
||||||
|
os.system("nosetests --where=tests")
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ PyTester class ]
|
||||||
|
# run py.test test runner in the tests directory
|
||||||
|
# Optional specify the number of directory levels to search bottom to top (default = 4)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class PyTester:
|
||||||
|
def __init__(self, dir_levels = 6):
|
||||||
|
self.number_of_dir_levels = dir_levels
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
py_found = False
|
||||||
|
for i in range(self.number_of_dir_levels):
|
||||||
|
if not self._is_testdir_at_this_level():
|
||||||
|
os.chdir(os.pardir)
|
||||||
|
else:
|
||||||
|
py_found = True
|
||||||
|
self._run_pytests()
|
||||||
|
break
|
||||||
|
if not py_found:
|
||||||
|
stderr("Unable to locate your testing directory", 1)
|
||||||
|
else:
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
def _is_testdir_at_this_level(self):
|
||||||
|
if file_exists('setup.py'):
|
||||||
|
if dir_exists('tests'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _run_pytests(self):
|
||||||
|
os.chdir('tests')
|
||||||
|
os.system('py.test')
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ UnitTester class ]
|
||||||
|
# run Python unit tests with the built in unittest methods
|
||||||
|
# Optional specify the number of directory levels to search bottom to top (default = 4)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class UnitTester:
|
||||||
|
def __init__(self, the_unit_test, dir_levels = 6):
|
||||||
|
self.unittest = the_unit_test
|
||||||
|
self.number_of_dir_levels = dir_levels
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
unit_found = False
|
||||||
|
for i in range(self.number_of_dir_levels):
|
||||||
|
if not self._is_testdir_at_this_level():
|
||||||
|
os.chdir(os.pardir)
|
||||||
|
else:
|
||||||
|
unit_found = True
|
||||||
|
os.chdir('tests')
|
||||||
|
if file_exists(self.unittest):
|
||||||
|
self._run_unittest()
|
||||||
|
else:
|
||||||
|
stderr("The unit test file " + self.unittest + " could not be found in the tests directory.")
|
||||||
|
if not unit_found:
|
||||||
|
stderr("Unable to locate your testing directory", 1)
|
||||||
|
else:
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
def _is_testdir_at_this_level(self):
|
||||||
|
if file_exists('setup.py'):
|
||||||
|
if dir_exists('tests'):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _run_unittest(self):
|
||||||
|
cmd_string = "python " + self.unittest
|
||||||
|
os.system(cmd_string)
|
||||||
|
|
||||||
|
|
||||||
|
def help():
|
||||||
|
help_string = """
|
||||||
|
Naked test Command Help
|
||||||
|
=======================
|
||||||
|
The test command allows you to run unit tests from any working directory in your project.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
naked test <secondary command> [argument]
|
||||||
|
|
||||||
|
SECONDARY COMMANDS
|
||||||
|
nose - run the nose test runner on your project
|
||||||
|
pytest - run the py.test test runner on your project
|
||||||
|
tox - run the tox test runner on your project
|
||||||
|
unittest - run Python unit tests (built-in)
|
||||||
|
|
||||||
|
ARGUMENTS
|
||||||
|
nose
|
||||||
|
-- does not take additional arguments
|
||||||
|
|
||||||
|
pytest
|
||||||
|
-- does not take additional arguments
|
||||||
|
|
||||||
|
tox [python version]
|
||||||
|
-- You can include an optional tox Python version argument to run your
|
||||||
|
tests with a single version of Python (instead of the versions
|
||||||
|
specified in the tox.ini file). By default, the versions specified
|
||||||
|
in your tox.ini file are run.
|
||||||
|
|
||||||
|
unittest <test file>
|
||||||
|
-- Mandatory unit test file path (relative to the tests directory)
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
none
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
naked test nose
|
||||||
|
naked test pytest
|
||||||
|
naked test tox
|
||||||
|
naked test tox py27
|
||||||
|
naked test unittest test_app.py
|
||||||
|
|
||||||
|
A bottom to top search (from the working directory) is performed over up to 6 directory levels to find the 'tests' directory."""
|
||||||
|
print(help_string)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import Naked.settings
|
||||||
|
from Naked.toolshed.system import exit_success
|
||||||
|
|
||||||
|
class Usage:
|
||||||
|
def __init__(self):
|
||||||
|
self.usage = Naked.settings.usage
|
||||||
|
|
||||||
|
def print_usage(self):
|
||||||
|
print(self.usage)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import Naked.settings
|
||||||
|
from Naked.toolshed.system import exit_success
|
||||||
|
|
||||||
|
class Version:
|
||||||
|
def __init__(self):
|
||||||
|
self.major_version = Naked.settings.major_version
|
||||||
|
self.minor_version = Naked.settings.minor_version
|
||||||
|
self.patch_version = Naked.settings.patch_version
|
||||||
|
self.name = Naked.settings.app_name
|
||||||
|
self.app_version_string = self.name + " " + self.major_version + "." + self.minor_version + "." + self.patch_version
|
||||||
|
self.version_string = self.major_version + "." + self.minor_version + "." + self.patch_version
|
||||||
|
|
||||||
|
def print_version(self):
|
||||||
|
print(self.app_version_string)
|
||||||
|
exit_success()
|
||||||
|
|
||||||
|
def get_version(self):
|
||||||
|
return self.version_string
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# import sys
|
||||||
|
# from functools import wraps
|
||||||
|
# from Naked.toolshed.types import XMaxHeap, XMinHeap
|
||||||
|
|
||||||
|
# def print_scratch(func):
|
||||||
|
# @wraps(func)
|
||||||
|
# def print_wrapper(*args, **kwargs):
|
||||||
|
# print(func(*args, **kwargs))
|
||||||
|
# return print_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
# def run_scratchpad():
|
||||||
|
# # from Naked.toolshed.file import FileReader
|
||||||
|
# # from Naked.toolshed.types import XString, XUnicode
|
||||||
|
|
||||||
|
# # r = FileReader('/Users/ces/Desktop/code/naked/tests/testfiles/unicode.txt')
|
||||||
|
# # test1 = r.read_utf8()
|
||||||
|
# # test2 = XUnicode(test1, {'a': 'b'})
|
||||||
|
# # test3 = "Hey! It's Bengali ব য,and here is some more ২"
|
||||||
|
# # print(unicode(test2))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# if __name__ == '__main__':
|
||||||
|
# run_scratchpad()
|
@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Application Name
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
app_name = "naked"
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Version Number
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
major_version = "0"
|
||||||
|
minor_version = "1"
|
||||||
|
patch_version = "31"
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Debug Flag (switch to False for production release code)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
debug = False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Usage String
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
usage = """
|
||||||
|
Usage: naked <primary command> [secondary command] [option(s)] [argument(s)]
|
||||||
|
--- Use 'naked help' for detailed help ---
|
||||||
|
"""
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Help String
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
help = """
|
||||||
|
---------------------------------------------------
|
||||||
|
Naked
|
||||||
|
A Python command line application framework
|
||||||
|
Copyright 2014 Christopher Simpkins
|
||||||
|
MIT license
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
ABOUT
|
||||||
|
|
||||||
|
The Naked framework includes the "naked" executable and the Python toolshed library. The naked executable is a command line tool for application development, testing, profiling, and deployment. The toolshed library contains numerous useful tools for application development that can be used through standard Python module imports. These features are detailed in the documentation (link below).
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
|
||||||
|
The naked executable syntax is:
|
||||||
|
|
||||||
|
naked <primary command> [secondary command] [option(s)] [argument(s)]
|
||||||
|
|
||||||
|
The <primary command> is mandatory and includes one of the commands in the following section. The [bracketed] syntax structure is optional and dependent upon the primary command that you use. Use the command 'naked <primary command> help' for details about a command.
|
||||||
|
|
||||||
|
PRIMARY COMMANDS SECONDARY COMMANDS
|
||||||
|
|
||||||
|
args help
|
||||||
|
build help
|
||||||
|
classify help
|
||||||
|
dist all•help•sdist•swheel•wheel•win
|
||||||
|
help - none -
|
||||||
|
locate main•help•settings•setup
|
||||||
|
make help
|
||||||
|
profile help
|
||||||
|
pyh help
|
||||||
|
test nose•pytest•tox•unittest
|
||||||
|
usage - none -
|
||||||
|
version - none -
|
||||||
|
|
||||||
|
HELP
|
||||||
|
|
||||||
|
To learn more about a primary command, use the following syntax:
|
||||||
|
|
||||||
|
naked <primary command> help
|
||||||
|
|
||||||
|
DOCUMENTATION
|
||||||
|
|
||||||
|
http://docs.naked-py.com
|
||||||
|
|
||||||
|
SOURCE REPOSITORY
|
||||||
|
|
||||||
|
https://github.com/chrissimpkins/naked
|
||||||
|
|
||||||
|
ISSUE REPORTING
|
||||||
|
|
||||||
|
https://github.com/chrissimpkins/naked/issues
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# VARS = app_name, developer, license_name, year
|
||||||
|
app_file_string = """
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# {{app_name}}
|
||||||
|
# Copyright {{year}} {{developer}}
|
||||||
|
# {{license}}
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------
|
||||||
|
# c.cmd = Primary command ({{app_name}} <primary command>)
|
||||||
|
# c.cmd2 = Secondary command ({{app_name}} <primary command> <secondary command>)
|
||||||
|
#
|
||||||
|
# c.arg_to_cmd = first positional argument to the primary command
|
||||||
|
# c.arg_to_cmd2 = first positional argument to the secondary command
|
||||||
|
#
|
||||||
|
# c.option(option_string, [bool argument_required]) = test for option with optional positional argument to option test
|
||||||
|
# c.option_with_arg(option_string) = test for option and mandatory positional argument to option
|
||||||
|
# c.flag(flag_string) = test for presence of a "option=argument" style flag
|
||||||
|
#
|
||||||
|
# c.arg(arg_string) = returns the next positional argument to the arg_string argument
|
||||||
|
# c.flag_arg(flag_string) = returns the flag assignment for a "--option=argument" style flag
|
||||||
|
#------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Application start
|
||||||
|
def main():
|
||||||
|
import sys
|
||||||
|
from Naked.commandline import Command
|
||||||
|
from Naked.toolshed.state import StateObject
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ Instantiate command line object ]
|
||||||
|
# used for all subsequent conditional logic in the CLI application
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
c = Command(sys.argv[0], sys.argv[1:])
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ Instantiate state object ]
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
state = StateObject()
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ Command Suite Validation ] - early validation of appropriate command syntax
|
||||||
|
# Test that user entered at least one argument to the executable, print usage if not
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
if not c.command_suite_validates():
|
||||||
|
from {{app_name}}.settings import usage as {{app_name}}_usage
|
||||||
|
print({{app_name}}_usage)
|
||||||
|
sys.exit(1)
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ NAKED FRAMEWORK COMMANDS ]
|
||||||
|
# Naked framework provides default help, usage, and version commands for all applications
|
||||||
|
# --> settings for user messages are assigned in the lib/{{app_name}}/settings.py file
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
if c.help(): # User requested {{app_name}} help information
|
||||||
|
from {{app_name}}.settings import help as {{app_name}}_help
|
||||||
|
print({{app_name}}_help)
|
||||||
|
sys.exit(0)
|
||||||
|
elif c.usage(): # User requested {{app_name}} usage information
|
||||||
|
from {{app_name}}.settings import usage as {{app_name}}_usage
|
||||||
|
print({{app_name}}_usage)
|
||||||
|
sys.exit(0)
|
||||||
|
elif c.version(): # User requested {{app_name}} version information
|
||||||
|
from {{app_name}}.settings import app_name, major_version, minor_version, patch_version
|
||||||
|
version_display_string = app_name + ' ' + major_version + '.' + minor_version + '.' + patch_version
|
||||||
|
print(version_display_string)
|
||||||
|
sys.exit(0)
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ PRIMARY COMMAND LOGIC ]
|
||||||
|
# Enter your command line parsing logic below
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# [[ Example usage ]] ------------------------------->>>
|
||||||
|
# if c.cmd == 'hello':
|
||||||
|
# if c.cmd2 = 'world':
|
||||||
|
# if c.option('--print'):
|
||||||
|
# print('Hello World!')
|
||||||
|
# elif c.cmd == 'spam':
|
||||||
|
# if c.option_with_arg('--with'):
|
||||||
|
# friend_of_spam = c.arg('--with') # user enters {{app_name}} spam --with eggs
|
||||||
|
# print('spam and ' + friend_of_spam) # prints 'spam and eggs'
|
||||||
|
# elif c.cmd == 'naked':
|
||||||
|
# if c.flag("--language"):
|
||||||
|
# lang = c.flag_arg("--language") # user enters {{app_name}} naked --language=python
|
||||||
|
# print("Naked & " + lang) # prints 'Naked & python'
|
||||||
|
# End example --------------------------------------->>>
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
# [ DEFAULT MESSAGE FOR MATCH FAILURE ]
|
||||||
|
# Message to provide to the user when all above conditional logic fails to meet a true condition
|
||||||
|
#------------------------------------------------------------------------------------------
|
||||||
|
else:
|
||||||
|
print("Could not complete the command that you entered. Please try again.")
|
||||||
|
sys.exit(1) #exit
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
"""
|
@ -0,0 +1,140 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# ALL LICENSES: VARS = year, developer
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ apache_license ]
|
||||||
|
# The Apache License
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
apache_license = """
|
||||||
|
Copyright {{year}} {{developer}}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ bsd_license ]
|
||||||
|
# The BSD 2-clause license
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
bsd_license = """
|
||||||
|
Copyright (c) {{year}}, {{developer}}
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ gpl3_license ]
|
||||||
|
# The GPL v3 License
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
gpl3_license = """
|
||||||
|
Copyright (C) {{year}} {{developer}}
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
"""
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [lgpl_license ]
|
||||||
|
# The Lesser GPL license
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
lgpl_license = """
|
||||||
|
Copyright (C) {{year}} {{developer}}
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||||
|
USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ mit_license ]
|
||||||
|
# The MIT License
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
mit_license = """
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) {{year}} {{developer}}
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ mozilla_license ]
|
||||||
|
# The Mozilla Public license, v2.0
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
mozilla_license = """
|
||||||
|
Mozilla Public License, v.2.0
|
||||||
|
|
||||||
|
Copyright (c) {{year}} {{developer}}
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0.
|
||||||
|
|
||||||
|
If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
"""
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# VARS = --none--
|
||||||
|
manifest_file_string = "recursive-include docs *"
|
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# VARS = --none--
|
||||||
|
profiler_file_string = """
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import cProfile, pstats, StringIO
|
||||||
|
|
||||||
|
def profile():
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Setup a profile
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
pr = cProfile.Profile()
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Enter setup code below
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Optional: include setup code here
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Start profiler
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
pr.enable()
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# BEGIN profiled code block
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# include profiled code here
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# END profiled code block
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
pr.disable()
|
||||||
|
s = StringIO.StringIO()
|
||||||
|
sortby = 'cumulative'
|
||||||
|
ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
|
||||||
|
ps.strip_dirs().sort_stats("time").print_stats()
|
||||||
|
print(s.getvalue())
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
profile()
|
||||||
|
"""
|
@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# VARS = year
|
||||||
|
pypush_file_string = """
|
||||||
|
#!/bin/sh
|
||||||
|
# Scriptacular - pypush.sh
|
||||||
|
# Create a Python source distribution and push it to PyPI
|
||||||
|
# Copyright {{year}} Christopher Simpkins
|
||||||
|
# MIT License
|
||||||
|
|
||||||
|
|
||||||
|
# Build and push to PyPI
|
||||||
|
python setup.py sdist upload
|
||||||
|
|
||||||
|
# Confirm that it worked
|
||||||
|
if (( $? )); then
|
||||||
|
echo "Unable to distribute your release to PyPI" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
python setup.py bdist_wheel upload
|
||||||
|
|
||||||
|
# Confirm that wheel distribution worked
|
||||||
|
if (( $? )); then
|
||||||
|
echo "Unable to distribute your wheel to PyPI" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Exit success
|
||||||
|
exit 0
|
||||||
|
"""
|
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# VARS: app_name
|
||||||
|
readme_md_string = """
|
||||||
|
{{app_name}}
|
||||||
|
=====
|
||||||
|
"""
|
@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# VARS = app_name
|
||||||
|
settings_file_string = """
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Application Name
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
app_name = '{{app_name}}'
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Version Number
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
major_version = "0"
|
||||||
|
minor_version = "1"
|
||||||
|
patch_version = "0"
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Debug Flag (switch to False for production release code)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
debug = True
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Usage String
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
usage = ''
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Help String
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
help = ''
|
||||||
|
"""
|
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# VARS: --none--
|
||||||
|
setup_cfg_string = """
|
||||||
|
[wheel]
|
||||||
|
universal = 1
|
||||||
|
"""
|
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
# VARS: app_name, developer, license
|
||||||
|
setup_py_string = ("""
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
|
||||||
|
def docs_read(fname):
|
||||||
|
return open(os.path.join(os.path.dirname(__file__), 'docs', fname)).read()
|
||||||
|
|
||||||
|
def version_read():
|
||||||
|
settings_file = open(os.path.join(os.path.dirname(__file__), 'lib', '{{app_name}}', 'settings.py')).read()
|
||||||
|
major_regex = """ + '"""' + """major_version\s*?=\s*?["']{1}(\d+)["']{1}""" + '"""' + '\n ' +
|
||||||
|
'minor_regex = ' + '"""' + """minor_version\s*?=\s*?["']{1}(\d+)["']{1}""" + '"""' + '\n ' +
|
||||||
|
'patch_regex = ' + '"""' + """patch_version\s*?=\s*?["']{1}(\d+)["']{1}""" + '"""' + '\n ' +
|
||||||
|
"""major_match = re.search(major_regex, settings_file)
|
||||||
|
minor_match = re.search(minor_regex, settings_file)
|
||||||
|
patch_match = re.search(patch_regex, settings_file)
|
||||||
|
major_version = major_match.group(1)
|
||||||
|
minor_version = minor_match.group(1)
|
||||||
|
patch_version = patch_match.group(1)
|
||||||
|
if len(major_version) == 0:
|
||||||
|
major_version = 0
|
||||||
|
if len(minor_version) == 0:
|
||||||
|
minor_version = 0
|
||||||
|
if len(patch_version) == 0:
|
||||||
|
patch_version = 0
|
||||||
|
return major_version + "." + minor_version + "." + patch_version
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='{{app_name}}',
|
||||||
|
version=version_read(),
|
||||||
|
description='',
|
||||||
|
long_description=(docs_read('README.rst')),
|
||||||
|
url='',
|
||||||
|
license='{{license}}',
|
||||||
|
author='{{developer}}',
|
||||||
|
author_email='',
|
||||||
|
platforms=['any'],
|
||||||
|
entry_points = {
|
||||||
|
'console_scripts': [
|
||||||
|
'{{app_name}} = {{app_name}}.app:main'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
packages=find_packages("lib"),
|
||||||
|
package_dir={'': 'lib'},
|
||||||
|
install_requires=['Naked'],
|
||||||
|
keywords='',
|
||||||
|
include_package_data=True,
|
||||||
|
classifiers=[],
|
||||||
|
)
|
||||||
|
""")
|
@ -0,0 +1,463 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import gc
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ timer function decorator ]
|
||||||
|
# runs timed repetitions of the decorated function in a single trial
|
||||||
|
# default is 100,000 repetitions in the trial
|
||||||
|
# reports the results of the trial
|
||||||
|
# Usage example:
|
||||||
|
# from Naked.toolshed.benchmarking import timer
|
||||||
|
# @timer
|
||||||
|
# def myfunction():
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def timer(func, repetitions=100000):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ timer_X function decorators ]
|
||||||
|
# replicate the above decorator with different number of repetitions
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def timer_10(func, repetitions=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_100(func, repetitions=100):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_1k(func, repetitions=1000):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_10k(func, repetitions=10000):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_1m(func, repetitions=1000000):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ timer_trials_benchmark decorator function ]
|
||||||
|
# time a function and compare to a benchmark function
|
||||||
|
# default is 10 trials x 100,000 repetitions/trial for each function
|
||||||
|
# Results:
|
||||||
|
# - Mean of the 10 trials of the test function
|
||||||
|
# - standard deviation of the 10 trials (if numpy is available)
|
||||||
|
# - result for 100,000 repetitions of the benchmark function
|
||||||
|
# - ratio of the test : benchmark function results
|
||||||
|
# Usage example:
|
||||||
|
# from Naked.toolshed.benchmarking import timer_trials_benchmark
|
||||||
|
# @timer_trials_benchmark
|
||||||
|
# def myfunction():
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def timer_trials_benchmark(func, repetitions=100000, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ timer_trials_benchmark_X decorators ]
|
||||||
|
# additional benchmark decorators that replicate the above function with different # repetitions
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def timer_trials_benchmark_10(func, repetitions=10, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_trials_benchmark_100(func, repetitions=100, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_trials_benchmark_1k(func, repetitions=1000, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_trials_benchmark_10k(func, repetitions=10000, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_trials_benchmark_1m(func, repetitions=1000000, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,463 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import gc
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ timer function decorator ]
|
||||||
|
# runs timed repetitions of the decorated function in a single trial
|
||||||
|
# default is 100,000 repetitions in the trial
|
||||||
|
# reports the results of the trial
|
||||||
|
# Usage example:
|
||||||
|
# from Naked.toolshed.benchmarking import timer
|
||||||
|
# @timer
|
||||||
|
# def myfunction():
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def timer(func, repetitions=100000):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ timer_X function decorators ]
|
||||||
|
# replicate the above decorator with different number of repetitions
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def timer_10(func, repetitions=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_100(func, repetitions=100):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_1k(func, repetitions=1000):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_10k(func, repetitions=10000):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_1m(func, repetitions=1000000):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting " + str(repetitions) + " repetitions of " + func.__name__ + "()...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
print(" ")
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
start = time.time()
|
||||||
|
for x in range(repetitions):
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(str(repetitions) + " repetitions of " + func.__name__ + " : " + str(end-start) + " sec")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ timer_trials_benchmark decorator function ]
|
||||||
|
# time a function and compare to a benchmark function
|
||||||
|
# default is 10 trials x 100,000 repetitions/trial for each function
|
||||||
|
# Results:
|
||||||
|
# - Mean of the 10 trials of the test function
|
||||||
|
# - standard deviation of the 10 trials (if numpy is available)
|
||||||
|
# - result for 100,000 repetitions of the benchmark function
|
||||||
|
# - ratio of the test : benchmark function results
|
||||||
|
# Usage example:
|
||||||
|
# from Naked.toolshed.benchmarking import timer_trials_benchmark
|
||||||
|
# @timer_trials_benchmark
|
||||||
|
# def myfunction():
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def timer_trials_benchmark(func, repetitions=100000, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ timer_trials_benchmark_X decorators ]
|
||||||
|
# additional benchmark decorators that replicate the above function with different # repetitions
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def timer_trials_benchmark_10(func, repetitions=10, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_trials_benchmark_100(func, repetitions=100, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_trials_benchmark_1k(func, repetitions=1000, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_trials_benchmark_10k(func, repetitions=10000, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def timer_trials_benchmark_1m(func, repetitions=1000000, trials=10):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
sys.stdout.write("Starting timed trials of " + func.__name__ + "()")
|
||||||
|
sys.stdout.flush()
|
||||||
|
result_list = []
|
||||||
|
benchmark_list = []
|
||||||
|
# disable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
gc.disable()
|
||||||
|
for x in range(trials):
|
||||||
|
# test function
|
||||||
|
start = time.time()
|
||||||
|
for y in range(repetitions):
|
||||||
|
func(*args, **kwargs)
|
||||||
|
end = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
result_list.append(end-start)
|
||||||
|
# benchmark function
|
||||||
|
L = []
|
||||||
|
start2 = time.time()
|
||||||
|
for j in range(repetitions):
|
||||||
|
for i in range(10):
|
||||||
|
L.append(i)
|
||||||
|
end2 = time.time()
|
||||||
|
benchmark_list.append(end2 - start2)
|
||||||
|
sys.stdout.write(".")
|
||||||
|
sys.stdout.flush()
|
||||||
|
gc.enable() # re-enable garbage collection
|
||||||
|
gc.collect()
|
||||||
|
print(" ")
|
||||||
|
n = 1
|
||||||
|
for run in result_list:
|
||||||
|
print("Trial " + str(n) + ":\t" + str(run))
|
||||||
|
n += 1
|
||||||
|
print("-"*50)
|
||||||
|
mean = sum(result_list)/len(result_list)
|
||||||
|
mean_benchmark = sum(benchmark_list)/len(benchmark_list)
|
||||||
|
print("Mean for " + str(repetitions) + " repetitions: " + str(mean) + " sec")
|
||||||
|
try:
|
||||||
|
import numpy as np
|
||||||
|
array = np.array(result_list)
|
||||||
|
print( "Standard Deviation: " + str(np.std(array)))
|
||||||
|
except ImportError as ie:
|
||||||
|
pass
|
||||||
|
print("Mean per repetition: " + str(mean/repetitions) + " sec")
|
||||||
|
print("Mean for " + str(repetitions) + " of benchmark function:" + str(mean_benchmark) + " sec")
|
||||||
|
print("Ratio: " + str(mean/mean_benchmark))
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1 @@
|
|||||||
|
python setup.py build_ext --inplace
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,117 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# cython: profile=True
|
||||||
|
|
||||||
|
cdef inline int increment(object iterable, object test_string):
|
||||||
|
cdef int count = 0
|
||||||
|
cdef object thing
|
||||||
|
for thing in iterable:
|
||||||
|
if thing == test_string:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
from Naked.toolshed.c.types import NakedObject, XFSet, XDict, XList, XQueue, XSet, XString
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ nob ] (NakedObject)
|
||||||
|
# Cast a dictionary of attributes to a NakedObject with key>attribute mapping
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def nob(attributes={}):
|
||||||
|
try:
|
||||||
|
return NakedObject(attributes)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to create a NakedObject with the requested argument using the nobj() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xd function ] (XDict)
|
||||||
|
# Cast a Python dictionary to a XDict
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xd(dictionary_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XDict(dictionary_arg, attributes)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError("Attempted to cast to a XDict with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XDict with the xd() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xl function ] (XList)
|
||||||
|
# Cast a Python list, set, or tuple to a XList
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xl(list_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XList(list_arg, attributes)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError("Attempted to cast to a XList with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XList with the xl() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xq function ] (XQueue)
|
||||||
|
# Cast a Python list, set, tuple to a XQueue
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xq(queue_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XQueue(queue_arg, attributes)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError("Attempted to cast to a XQueue with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XQueue with the xq() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xset function ] (XSet)
|
||||||
|
# Cast a Python set to a XSet
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xset(set_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XSet(set_arg, attributes)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError("Attempted to cast to a XSet with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XSet with the xset() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xfset function ] (XFSet)
|
||||||
|
# Cast a Python set to a XFSet
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xfset(set_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XFSet(set_arg, attributes)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError("Attempted to cast to a XSet with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XSet with the xset() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xstr function ] (XString)
|
||||||
|
# Cast a Python string to a XString
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xstr(string_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XString(string_arg, attributes)
|
||||||
|
except TypeError as te:
|
||||||
|
raise TypeError("Attempted to cast to a XString with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XString with the xstr() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test = xstr("A test", {'arg1': '2'})
|
||||||
|
print(test)
|
||||||
|
print(test[0])
|
||||||
|
print(test.arg1)
|
||||||
|
print(type(test))
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# cython: profile=False
|
||||||
|
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
from Naked.toolshed.c.system import cwd
|
||||||
|
import Naked.toolshed.c.python as py
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
class StateObject:
|
||||||
|
def __init__(self):
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
self.py2 = py.is_py2() #truth test Python 2 interpreter
|
||||||
|
self.py3 = py.is_py3() #truth test Python 3 interpreter
|
||||||
|
self.py_major = py.py_major_version() #Python major version
|
||||||
|
self.py_minor = py.py_minor_version() #Python minor version
|
||||||
|
self.py_patch = py.py_patch_version() #Python patch version
|
||||||
|
self.os = sys.platform #user operating system
|
||||||
|
self.cwd = cwd() #current (present) working directory
|
||||||
|
self.parent_dir = os.pardir
|
||||||
|
self.default_path = os.defpath
|
||||||
|
self.user_path = os.path.expanduser("~")
|
||||||
|
self.string_encoding = sys.getdefaultencoding()
|
||||||
|
self.file_encoding = sys.getfilesystemencoding()
|
||||||
|
self.hour = now.hour
|
||||||
|
self.min = now.minute
|
||||||
|
self.year = now.year
|
||||||
|
self.day = now.day
|
||||||
|
self.month = now.month
|
||||||
|
self.second = now.second
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,15 @@
|
|||||||
|
echo "Cythonizing .pyx files to C files"
|
||||||
|
|
||||||
|
cython benchmarking.pyx
|
||||||
|
cython casts.pyx
|
||||||
|
cython file.pyx
|
||||||
|
cython ink.pyx
|
||||||
|
cython network.pyx
|
||||||
|
cython python.pyx
|
||||||
|
cython shell.pyx
|
||||||
|
cython cstate.pyx
|
||||||
|
cython system.pyx
|
||||||
|
cython types.pyx
|
||||||
|
|
||||||
|
|
||||||
|
echo "Cythonization is complete"
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,377 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# cython: profile=False
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ IO class ]
|
||||||
|
# interface for all local file IO classes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class IO:
|
||||||
|
def __init__(self,filepath):
|
||||||
|
self.filepath = filepath
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ FileWriter class ]
|
||||||
|
# writes data to local files
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class FileWriter(IO):
|
||||||
|
def __init__(self, filepath):
|
||||||
|
IO.__init__(self, filepath)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ append method ]
|
||||||
|
# Universal text file writer that appends to existing file using system default text encoding or utf-8 if throws unicode error
|
||||||
|
# Tests: test_IO.py:: test_file_ascii_readwrite_append, test_file_append_missingfile
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def append(self, text):
|
||||||
|
try:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if not file_exists(self.filepath): #confirm that file exists, if not raise IOError (assuming that developer expected existing file if using append)
|
||||||
|
raise IOError("The file specified for the text append does not exist (Naked.toolshed.file.py:append).")
|
||||||
|
with open(self.filepath, 'a') as appender:
|
||||||
|
appender.write(text)
|
||||||
|
except UnicodeEncodeError as ue:
|
||||||
|
self.append_utf8(text) #try writing as utf-8
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to append text to the file with the append() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ append_utf8 method ]
|
||||||
|
# Text writer that appends text to existing file with utf-8 encoding
|
||||||
|
# Tests: test_IO.py :: test_file_utf8_readwrite_append
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def append_utf8(self, text):
|
||||||
|
try:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if not file_exists(self.filepath):
|
||||||
|
raise IOError("The file specified for the text append does not exist (Naked.toolshed.file.py:append_utf8).")
|
||||||
|
import codecs
|
||||||
|
import unicodedata
|
||||||
|
norm_text = unicodedata.normalize('NFKD', text) # NKFD normalization of the unicode data before write
|
||||||
|
with codecs.open(self.filepath, mode='a', encoding="utf_8") as appender:
|
||||||
|
appender.write(norm_text)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to append text to the file with the append_utf8 method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ gzip method (writer) ]
|
||||||
|
# writes data to gzip compressed file
|
||||||
|
# Note: adds .gz extension to filename if user did not specify it in the FileWriter class constructor
|
||||||
|
# Note: uses compresslevel = 6 as default to balance speed and compression level (which in general is not significantly less than 9)
|
||||||
|
# Tests: test_IO.py :: test_file_gzip_ascii_readwrite, test_file_gzip_utf8_readwrite,
|
||||||
|
# test_file_gzip_utf8_readwrite_explicit_decode
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def gzip(self, text, compression_level=6):
|
||||||
|
try:
|
||||||
|
import gzip
|
||||||
|
if not self.filepath.endswith(".gz"):
|
||||||
|
self.filepath = self.filepath + ".gz"
|
||||||
|
with gzip.open(self.filepath, 'wb', compresslevel=compression_level) as gzip_writer:
|
||||||
|
gzip_writer.write(text)
|
||||||
|
except UnicodeEncodeError as ue:
|
||||||
|
import unicodedata
|
||||||
|
norm_text = unicodedata.normalize('NFKD', text) # NKFD normalization of the unicode data before write
|
||||||
|
import codecs
|
||||||
|
binary_data = codecs.encode(norm_text, "utf_8")
|
||||||
|
with gzip.open(self.filepath, 'wb', compresslevel=compression_level) as gzip_writer:
|
||||||
|
gzip_writer.write(binary_data)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to gzip compress the file with the gzip method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ write method ]
|
||||||
|
# Universal text file writer that writes by system default or utf-8 encoded unicode if throws UnicdeEncodeError
|
||||||
|
# Tests: test_IO.py :: test_file_ascii_readwrite, test_file_ascii_readwrite_missing_file,
|
||||||
|
# test_file_utf8_write_raises_unicodeerror
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def write(self, text):
|
||||||
|
try:
|
||||||
|
with open(self.filepath, 'wt') as writer:
|
||||||
|
writer.write(text)
|
||||||
|
except UnicodeEncodeError as ue:
|
||||||
|
self.write_utf8(text) # attempt to write with utf-8 encoding
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to write to requested file with the write() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ write_as method ]
|
||||||
|
# text file writer that uses developer specified text encoding
|
||||||
|
# Tests: test_IO.py :: test_file_utf8_readas_writeas
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def write_as(self, text, the_encoding=""):
|
||||||
|
try:
|
||||||
|
if the_encoding == "": #if the developer did not include the encoding type, raise an exception
|
||||||
|
raise RuntimeError("The text encoding was not specified as an argument to the write_as() method (Naked.toolshed.file.py:write_as).")
|
||||||
|
import codecs
|
||||||
|
with codecs.open(self.filepath, encoding=the_encoding, mode='w') as f:
|
||||||
|
f.write(text)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to write file with the specified encoding using the write_as() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ write_bin method ]
|
||||||
|
# binary data file writer
|
||||||
|
# Tests: test_IO.py :: test_file_bin_readwrite
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def write_bin(self, binary_data):
|
||||||
|
try:
|
||||||
|
with open(self.filepath, 'wb') as bin_writer:
|
||||||
|
bin_writer.write(binary_data)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to write binary data to file with the write_bin method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ safe_write method ] (boolean)
|
||||||
|
# Universal text file writer (writes in default encoding unless throws unicode error) that will NOT overwrite existing file at the requested filepath
|
||||||
|
# returns boolean indicator for success of write based upon test for existence of file (False = write failed because file exists)
|
||||||
|
# Tests: test_IO.py :: test_file_ascii_safewrite, test_file_utf8_safewrite
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def safe_write(self, text):
|
||||||
|
import os.path
|
||||||
|
if not os.path.exists(self.filepath): # if the file does not exist, then can write
|
||||||
|
try:
|
||||||
|
with open(self.filepath, 'wt') as writer:
|
||||||
|
writer.write(text)
|
||||||
|
return True
|
||||||
|
except UnicodeEncodeError as ue:
|
||||||
|
self.write_utf8(text)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to write to requested file with the safe_write() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
else:
|
||||||
|
return False # if file exists, do not write and return False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ safe_write_bin method ]
|
||||||
|
# Binary data file writer that will NOT overwrite existing file at the requested filepath
|
||||||
|
# returns boolean indicator for success of write based upon test for existence of file (False = write failed because file exists)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def safe_write_bin(self, file_data):
|
||||||
|
try:
|
||||||
|
import os.path
|
||||||
|
if not os.path.exists(self.filepath):
|
||||||
|
with open(self.filepath, 'wb') as writer:
|
||||||
|
writer.write(file_data)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to write to requested file with the safe_write_bin() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ write_utf8 method ]
|
||||||
|
# Text file writer with explicit UTF-8 text encoding
|
||||||
|
# uses filepath from class constructor
|
||||||
|
# requires text to passed as a method parameter
|
||||||
|
# Tests: test_IO.py :: test_file_utf8_readwrite, test_file_utf8_readwrite_raises_unicodeerror
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def write_utf8(self, text):
|
||||||
|
try:
|
||||||
|
import codecs
|
||||||
|
f = codecs.open(self.filepath, encoding='utf_8', mode='w')
|
||||||
|
except IOError as ioe:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to open file for write with the write_utf8() method (Naked.toolshed.file.py).")
|
||||||
|
raise ioe
|
||||||
|
try:
|
||||||
|
import unicodedata
|
||||||
|
norm_text = unicodedata.normalize('NFKD', text) # NKFD normalization of the unicode data before write
|
||||||
|
f.write(norm_text)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to write UTF-8 encoded text to file with the write_utf8() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ FileReader class ]
|
||||||
|
# reads data from local files
|
||||||
|
# filename assigned in constructor (inherited from IO class interface)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class FileReader(IO):
|
||||||
|
def __init__(self, filepath):
|
||||||
|
IO.__init__(self, filepath)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ read method ] (string)
|
||||||
|
# Universal text file reader that will read utf-8 encoded unicode or non-unicode text as utf-8
|
||||||
|
# returns string or unicode (py3 = string for unicode and non-unicode, py2 = str for non-unicode, unicode for unicode)
|
||||||
|
# Tests: test_IO.py :: test_file_ascii_readwrite, test_file_read_missing_file,
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def read(self):
|
||||||
|
try:
|
||||||
|
return self.read_utf8() #reads everything as unicode in utf8 encoding
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read text from the requested file with the read() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ read_bin method ] (binary byte string)
|
||||||
|
# Universal binary data file reader
|
||||||
|
# returns file contents in binary mode as binary byte strings
|
||||||
|
# Tests: test_IO.py :: test_file_bin_readwrite, test_file_read_bin_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def read_bin(self):
|
||||||
|
try:
|
||||||
|
with open(self.filepath, 'rb') as bin_reader:
|
||||||
|
data = bin_reader.read()
|
||||||
|
return data
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read the binary data from the file with the read_bin method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ read_as method ] (string with developer specified text encoding)
|
||||||
|
# Text file reader with developer specified text encoding
|
||||||
|
# returns file contents in developer specified text encoding
|
||||||
|
# Tests: test_IO.py :: test_file_utf8_readas_writeas, test_file_readas_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def read_as(self, the_encoding):
|
||||||
|
try:
|
||||||
|
if the_encoding == "":
|
||||||
|
raise RuntimeError("The text file encoding was not specified as an argument to the read_as method (Naked.toolshed.file.py:read_as).")
|
||||||
|
import codecs
|
||||||
|
with codecs.open(self.filepath, encoding=the_encoding, mode='r') as f:
|
||||||
|
data = f.read()
|
||||||
|
return data
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read the file with the developer specified text encoding with the read_as method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ readlines method ] (list of strings)
|
||||||
|
# Read text from file line by line, uses utf8 encoding by default
|
||||||
|
# returns list of utf8 encoded file lines as strings
|
||||||
|
# Tests: test_IO.py :: test_file_readlines, test_file_readlines_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def readlines(self):
|
||||||
|
try:
|
||||||
|
return self.readlines_utf8() # read as utf8 encoded file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read text from the requested file with the readlines() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ readlines_as method ] (list of developer specified encoded strings)
|
||||||
|
# Read lines from file with developer specified text encoding
|
||||||
|
# Returns a list of developer specified encoded lines from the file
|
||||||
|
# Tests: test_IO.py ::
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def readlines_as(self, dev_spec_encoding):
|
||||||
|
try:
|
||||||
|
if dev_spec_encoding == "":
|
||||||
|
raise RuntimeError("The text file encoding was not specified as an argument to the readlines_as method (Naked.toolshed.file.py:readlines_as).")
|
||||||
|
import codecs
|
||||||
|
with codecs.open(self.filepath, encoding=dev_spec_encoding, mode='r') as reader:
|
||||||
|
data_list = []
|
||||||
|
for line in reader:
|
||||||
|
data_list.append(line)
|
||||||
|
return data_list
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to read lines in the specified encoding with the readlines_as method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ readlines_utf8 method ] (list of utf-8 encoded strings)
|
||||||
|
# Read text from unicode file by line
|
||||||
|
# Returns list of file unicode text lines as unicode strings
|
||||||
|
# Tests: test_IO.py :: test_file_readlines_unicode, test_file_readlines_utf8_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def readlines_utf8(self):
|
||||||
|
try:
|
||||||
|
import codecs
|
||||||
|
with codecs.open(self.filepath, encoding='utf-8', mode='r') as uni_reader:
|
||||||
|
modified_text_list = []
|
||||||
|
for line in uni_reader:
|
||||||
|
import unicodedata
|
||||||
|
norm_line = unicodedata.normalize('NFKD', line) # NKFD normalization of the unicode data before use
|
||||||
|
modified_text_list.append(norm_line)
|
||||||
|
return modified_text_list
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to read lines in the unicode file with the readlines_utf8 method (Naked.toolshed.file.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ read_gzip ] (byte string)
|
||||||
|
# reads data from a gzip compressed file
|
||||||
|
# returns the decompressed binary data from the file
|
||||||
|
# Note: if decompressing unicode file, set encoding="utf-8"
|
||||||
|
# Tests: test_IO.py :: test_file_gzip_ascii_readwrite, test_file_gzip_utf8_readwrite,
|
||||||
|
# test_file_read_gzip_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def read_gzip(self, encoding="system_default"):
|
||||||
|
try:
|
||||||
|
import gzip
|
||||||
|
with gzip.open(self.filepath, 'rb') as gzip_reader:
|
||||||
|
file_data = gzip_reader.read()
|
||||||
|
if encoding in ["utf-8", "utf8", "utf_8", "UTF-8", "UTF8", "UTF_8"]:
|
||||||
|
import codecs
|
||||||
|
file_data = codecs.decode(file_data, "utf-8")
|
||||||
|
import unicodedata
|
||||||
|
norm_data = unicodedata.normalize('NFKD', file_data) # NKFD normalization of the unicode data before passing back to the caller
|
||||||
|
return norm_data
|
||||||
|
else:
|
||||||
|
return file_data
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read from the gzip compressed file with the read_gzip() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ read_utf8 method ] (string)
|
||||||
|
# read data from a file with explicit UTF-8 encoding
|
||||||
|
# uses filepath from class constructor
|
||||||
|
# returns a unicode string containing the file data (unicode in py2, str in py3)
|
||||||
|
# Tests: test_IO.py :: test_file_utf8_readwrite, test_file_utf8_readwrite_append,
|
||||||
|
# test_file_read_utf8_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def read_utf8(self):
|
||||||
|
try:
|
||||||
|
import codecs
|
||||||
|
f = codecs.open(self.filepath, encoding='utf_8', mode='r')
|
||||||
|
except IOError as ioe:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to open file for read with read_utf8() method (Naked.toolshed.file.py).")
|
||||||
|
raise ioe
|
||||||
|
try:
|
||||||
|
textstring = f.read()
|
||||||
|
import unicodedata
|
||||||
|
norm_text = unicodedata.normalize('NFKD', textstring) # NKFD normalization of the unicode data before returns
|
||||||
|
return norm_text
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read the file with UTF-8 encoding using the read_utf8() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,106 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# cython: profile=False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# The Ink Templating System
|
||||||
|
# A lightweight, fast, flexible text templating system
|
||||||
|
# Copyright 2014 Christopher Simpkins
|
||||||
|
# MIT License
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
import re
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Template class
|
||||||
|
# A template string class that is inherited from Python str
|
||||||
|
# Includes metadata about the template:
|
||||||
|
# odel = opening delimiter
|
||||||
|
# cdel = closing delimiter
|
||||||
|
# varlist = inclusive list of all variables in the template text (parsed in constructor)
|
||||||
|
# Delimiters:
|
||||||
|
# default = {{variable}}
|
||||||
|
# assign new opening and closing delimiters as parameters when you make a new Template instance
|
||||||
|
# `escape_regex` boolean is a speedup, avoids Python escape of special regex chars if you do not need it
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class Template(str):
|
||||||
|
def __new__(cls, template_text, open_delimiter="{{", close_delimiter="}}", escape_regex=False):
|
||||||
|
obj = str.__new__(cls, template_text)
|
||||||
|
obj.odel = open_delimiter
|
||||||
|
obj.cdel = close_delimiter
|
||||||
|
obj.varlist = obj._make_var_list(template_text, escape_regex) #contains all unique parsed variables from the template in a list
|
||||||
|
return obj
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _make_var_list method ] (list of strings)
|
||||||
|
# Private method that parses the template string for all variables that match the delimiter pattern
|
||||||
|
# Returns a list of the variable names as strings
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _make_var_list(self, template_text, escape_regex=False):
|
||||||
|
if escape_regex:
|
||||||
|
open_match_pat = self._escape_regex_special_chars(self.odel)
|
||||||
|
close_match_pat = self._escape_regex_special_chars(self.cdel)
|
||||||
|
match_pat = open_match_pat + r'(.*?)' + close_match_pat # capture group contains the variable name used between the opening and closing delimiters
|
||||||
|
else:
|
||||||
|
match_pat = self.odel + r'(.*?)' + self.cdel
|
||||||
|
var_list = re.findall(match_pat, template_text) #generate a list that contains the capture group from the matches (i.e. the variables in the template)
|
||||||
|
return set(var_list) # remove duplicate entries by converting to set (and lookup speed improvement from hashing)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _escape_regex_special_chars method ] (string)
|
||||||
|
# Private method that escapes special regex metacharacters
|
||||||
|
# Returns a string with the escaped character modifications
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _escape_regex_special_chars(self, test_escape_string):
|
||||||
|
return re.escape(test_escape_string)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Renderer class
|
||||||
|
# Render the variable replacements in the ink template using a Python dictionary key argument
|
||||||
|
# Construct the instace of the Renderer with the Ink template and the dictionary key
|
||||||
|
# Run the renderer with the render method on the instance (e.g. r.render())
|
||||||
|
# Parameters to constructor:
|
||||||
|
# - template = an Ink Template instance
|
||||||
|
# - key = a dictionary mapped key = variable name : value = variable replacement data
|
||||||
|
# - html_entities = encode html entities with HTML escaped characters (default = False = do not encode)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class Renderer:
|
||||||
|
def __init__(self, template, key, html_entities=False):
|
||||||
|
self.odel = template.odel
|
||||||
|
self.cdel = template.cdel
|
||||||
|
self.template = template
|
||||||
|
self.html_entities = html_entities
|
||||||
|
self.key_dict = key
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ render method ] (string)
|
||||||
|
# renders the variable replacements in the Ink template
|
||||||
|
# returns the rendered template as a string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def render(self):
|
||||||
|
# make local variables for the loop below (faster)
|
||||||
|
local_dict = self.key_dict
|
||||||
|
local_template = self.template
|
||||||
|
local_varlist = self.template.varlist
|
||||||
|
local_odel = self.odel
|
||||||
|
local_cdel = self.cdel
|
||||||
|
local_htmlent = self.html_entities
|
||||||
|
if local_htmlent:
|
||||||
|
from xml.sax.saxutils import escape #from Python std lib
|
||||||
|
for key in local_dict:
|
||||||
|
if key in local_varlist:
|
||||||
|
value = local_dict[key]
|
||||||
|
replace_string = local_odel + key + local_cdel
|
||||||
|
if local_htmlent:
|
||||||
|
value = escape(value) #xml.sax.saxutils function
|
||||||
|
local_template = local_template.replace(replace_string, value)
|
||||||
|
return local_template
|
||||||
|
|
||||||
|
##TODO : multiple file render method?
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
||||||
|
# template = Template("This is a of the {{test}} of the {{document}} {{type}} and more of the {{test}} {{document}} {{type}}")
|
||||||
|
# renderer = Renderer(template, {'test': 'ব য', 'document':'testing document', 'type':'of mine', 'bogus': 'bogus test'})
|
||||||
|
# print(renderer.render())
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,352 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# cython: profile=False
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import requests
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#[ HTTP class]
|
||||||
|
# handle HTTP requests
|
||||||
|
# Uses the requests external library to handle HTTP requests and response object (available on PyPI)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class HTTP():
|
||||||
|
def __init__(self, url="", request_timeout=10):
|
||||||
|
self.url = url
|
||||||
|
self.request_timeout = request_timeout
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# HTTP response properties (assignment occurs with the HTTP request methods)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
self.res = None # assigned with the requests external library response object after a HTTP method call
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get method ] (string) -
|
||||||
|
# HTTP GET request - returns text string
|
||||||
|
# returns data stream read from the URL (string)
|
||||||
|
# Default timeout = 10 s from class constructor
|
||||||
|
# Re-throws ConnectionError on failed connection (e.g. no site at URL)
|
||||||
|
# Test : test_NETWORK.py :: test_http_get, test_http_get_response_change,
|
||||||
|
# test_http_post_reponse_change, test_http_get_response_check
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get(self, follow_redirects=True):
|
||||||
|
try:
|
||||||
|
response = requests.get(self.url, timeout=self.request_timeout, allow_redirects=follow_redirects)
|
||||||
|
self.res = response # assign the response object from requests to a property on the instance of HTTP class
|
||||||
|
return response.text
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform GET request with the URL " + self.url + " using the get() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_data method ] (binary data)
|
||||||
|
# HTTP GET request, return binary data
|
||||||
|
# returns data stream with raw binary data
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_bin(self):
|
||||||
|
try:
|
||||||
|
response = requests.get(self.url, timeout=self.request_timeout)
|
||||||
|
self.res = response # assign the response object from requests to a property on the instance
|
||||||
|
return response.content # return binary data instead of text (get() returns text)
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform GET request with the URL " + self.url + " using the get_data() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_bin_write_file method ] (boolean)
|
||||||
|
# open HTTP data stream with GET request, make file with the returned binary data
|
||||||
|
# file path is passed to the method by the developer
|
||||||
|
# set suppress_output to True if you want to suppress the d/l status information that is printed to the standard output stream
|
||||||
|
# return True on successful pull and write to disk
|
||||||
|
# Tests: test_NETWORK.py :: test_http_get_binary
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_bin_write_file(self, filepath="", suppress_output = False, overwrite_existing = False):
|
||||||
|
try:
|
||||||
|
import os # used for os.fsync() method in the write
|
||||||
|
# Confirm that the file does not exist and prevent overwrite if it does (unless developer indicates otherwise)
|
||||||
|
if not overwrite_existing:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if file_exists(filepath):
|
||||||
|
if not suppress_output:
|
||||||
|
print("Download aborted. A local file with the requested filename exists on the path.")
|
||||||
|
return False
|
||||||
|
if (filepath == "" and len(self.url) > 1):
|
||||||
|
filepath = self.url.split('/')[-1] # use the filename from URL and working directory as default if not specified
|
||||||
|
if not suppress_output:
|
||||||
|
sys.stdout.write("Downloading file from " + self.url + "...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
response = requests.get(self.url, timeout=self.request_timeout, stream=True)
|
||||||
|
self.res = response
|
||||||
|
with open(filepath, 'wb') as f: # write as binary data
|
||||||
|
for chunk in response.iter_content(chunk_size=2048):
|
||||||
|
f.write(chunk)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno()) # flush all internal buffers to disk
|
||||||
|
if not suppress_output:
|
||||||
|
print(" ")
|
||||||
|
print("Download complete.")
|
||||||
|
return True # return True if successful write
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform GET request and write file with the URL " + self.url + " using the get_bin_write_file() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_txt_write_file method ] (boolean)
|
||||||
|
# open HTTP data stream with GET request, write file with utf-8 encoded text using returned text data
|
||||||
|
# file path is passed to the method by the developer (default is the base filename in the URL if not specified)
|
||||||
|
# return True on successful pull and write to disk
|
||||||
|
# Tests: test_NETWORK.py :: test_http_get_text
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_txt_write_file(self, filepath="", suppress_output = False, overwrite_existing = False):
|
||||||
|
try:
|
||||||
|
import os # used for os.fsync() method in the write
|
||||||
|
# Confirm that the file does not exist and prevent overwrite if it does (unless developer indicates otherwise)
|
||||||
|
if not overwrite_existing:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if file_exists(filepath):
|
||||||
|
if not suppress_output:
|
||||||
|
print("Download aborted. A local file with the requested filename exists on the path.")
|
||||||
|
return False
|
||||||
|
if (filepath == "" and len(self.url) > 1):
|
||||||
|
filepath = self.url.split('/')[-1] # use the filename from URL and working directory as default if not specified
|
||||||
|
if not suppress_output:
|
||||||
|
sys.stdout.write("Downloading file from " + self.url + "...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
response = requests.get(self.url, timeout=self.request_timeout, stream=True)
|
||||||
|
self.res = response
|
||||||
|
import codecs
|
||||||
|
with codecs.open(filepath, mode='w', encoding="utf-8") as f: #write as text
|
||||||
|
for chunk in response.iter_content(chunk_size=2048):
|
||||||
|
chunk = chunk.decode('utf-8')
|
||||||
|
f.write(chunk)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno()) # flush all internal buffers to disk
|
||||||
|
if not suppress_output:
|
||||||
|
print(" ")
|
||||||
|
print("Download complete.")
|
||||||
|
return True # return True if successful write
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform GET request and write file with the URL " + self.url + " using the get_data_write_txt() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ head method ] (dictionary of strings)
|
||||||
|
# HTTP HEAD request
|
||||||
|
# returns a dictionary of the header strings
|
||||||
|
# test for a specific header on either the response dictionary or the instance res property
|
||||||
|
# Usage example:
|
||||||
|
# content_type = instance.res['content-type']
|
||||||
|
# Tests: test_NETWORK.py :: test_http_head
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def head(self):
|
||||||
|
try:
|
||||||
|
response = requests.head(self.url, timeout=self.request_timeout)
|
||||||
|
self.res = response # assign the response object from requests to a property on the instance of HTTP class
|
||||||
|
return response.headers
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform a HEAD request with the head() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ post method ] (string)
|
||||||
|
# HTTP POST request for text
|
||||||
|
# returns text from the URL as a string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def post(self, follow_redirects=True):
|
||||||
|
try:
|
||||||
|
response = requests.post(self.url, timeout=self.request_timeout, allow_redirects=follow_redirects)
|
||||||
|
self.res = response # assign the response object from requests to a property on the instance of HTTP class
|
||||||
|
return response.text
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.exit("Naked Framework Error: Unable to perform a POST request with the post() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ post_bin method ] (binary data)
|
||||||
|
# HTTP POST request for binary data
|
||||||
|
# returns binary data from the URL
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def post_bin(self):
|
||||||
|
try:
|
||||||
|
response = requests.post(self.url, timeout=self.request_timeout)
|
||||||
|
self.res = response # assign the response object from requests to a property on the instance of HTTP class
|
||||||
|
return response.content
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.exit("Naked Framework Error: Unable to perform POST request with the post_bin() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ post_bin_write_file method ] (boolean = success of write)
|
||||||
|
# HTTP POST request, write binary file with the response data
|
||||||
|
# default filepath is the basename of the URL file, may be set by passing an argument to the method
|
||||||
|
# returns a boolean that indicates the success of the file write
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def post_bin_write_file(self, filepath="", suppress_output = False, overwrite_existing = False):
|
||||||
|
try:
|
||||||
|
import os # used for os.fsync() method in the write
|
||||||
|
# Confirm that the file does not exist and prevent overwrite if it does (unless developer indicates otherwise)
|
||||||
|
if not overwrite_existing:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if file_exists(filepath):
|
||||||
|
if not suppress_output:
|
||||||
|
print("Download aborted. A local file with the requested filename exists on the path.")
|
||||||
|
return False
|
||||||
|
if (filepath == "" and len(self.url) > 1):
|
||||||
|
filepath = self.url.split('/')[-1] # use the filename from URL and working directory as default if not specified
|
||||||
|
if not suppress_output:
|
||||||
|
sys.stdout.write("Downloading file from " + self.url + "...") #provide information about the download to user
|
||||||
|
sys.stdout.flush()
|
||||||
|
response = requests.post(self.url, timeout=self.request_timeout, stream=True)
|
||||||
|
self.res = response
|
||||||
|
with open(filepath, 'wb') as f: # write as binary data
|
||||||
|
for chunk in response.iter_content(chunk_size=2048):
|
||||||
|
f.write(chunk)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno()) # flush all internal buffers to disk
|
||||||
|
if not suppress_output:
|
||||||
|
print(" ")
|
||||||
|
print("Download complete.") # provide user with completion information
|
||||||
|
return True # return True if successful write
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform POST request and write file with the URL " + self.url + " using the post_data_write_bin() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ post_txt_write_file method ] (boolean = success of file write)
|
||||||
|
# HTTP POST request, write utf-8 encoded text file with the response data
|
||||||
|
# default filepath is the basename of the URL file, may be set by passing an argument to the method
|
||||||
|
# returns a boolean that indicates the success of the file write
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def post_txt_write_file(self, filepath="", suppress_output = False, overwrite_existing = False):
|
||||||
|
try:
|
||||||
|
import os # used for os.fsync() method in the write
|
||||||
|
# Confirm that the file does not exist and prevent overwrite if it does (unless developer indicates otherwise)
|
||||||
|
if not overwrite_existing:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if file_exists(filepath):
|
||||||
|
if not suppress_output:
|
||||||
|
print("Download aborted. A local file with the requested filename exists on the path.")
|
||||||
|
return False
|
||||||
|
if (filepath == "" and len(self.url) > 1):
|
||||||
|
filepath = self.url.split('/')[-1] # use the filename from URL and working directory as default if not specified
|
||||||
|
if not suppress_output:
|
||||||
|
sys.stdout.write("Downloading file from " + self.url + "...") #provide information about the download to user
|
||||||
|
sys.stdout.flush()
|
||||||
|
response = requests.post(self.url, timeout=self.request_timeout, stream=True)
|
||||||
|
self.res = response
|
||||||
|
import codecs
|
||||||
|
with codecs.open(filepath, mode='w', encoding="utf-8") as f: # write as binary data
|
||||||
|
for chunk in response.iter_content(chunk_size=2048):
|
||||||
|
chunk = chunk.decode('utf-8')
|
||||||
|
f.write(chunk)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno()) # flush all internal buffers to disk
|
||||||
|
if not suppress_output:
|
||||||
|
print(" ")
|
||||||
|
print("Download complete.") # provide user with completion information
|
||||||
|
return True # return True if successful write
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform POST request and write file with the URL " + self.url + " using the post_data_write_bin() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ response method ]
|
||||||
|
# getter method for the requests library object that is assigned as a property
|
||||||
|
# on the HTTP class after a HTTP request method is run (e.g. get())
|
||||||
|
# Note: must run one of the HTTP request verbs to assign this property before use of getter (=None by default)
|
||||||
|
# Tests: test_NETWORK.py :: test_http_get_response_check
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def response(self):
|
||||||
|
try:
|
||||||
|
return self.res
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to return the response from your HTTP request with the response() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_status_ok method ] (boolean)
|
||||||
|
# return boolean whether HTTP response was in 200 status code range for GET request
|
||||||
|
# Note: this method runs its own GET request, does not need to be run separately
|
||||||
|
# Tests: test_NETWORK.py ::
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_status_ok(self):
|
||||||
|
try:
|
||||||
|
self.get() #run the get request
|
||||||
|
if self.res and self.res.status_code:
|
||||||
|
return (self.res.status_code == requests.codes.ok)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to obtain the HTTP status with the get_status_ok() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ post_status_ok method ] (boolean)
|
||||||
|
# return boolean whether HTTP response was in 200 status code range for POST request
|
||||||
|
# Note: method runs its own post method, not necessary to run separately
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def post_status_ok(self):
|
||||||
|
try:
|
||||||
|
self.post() #run the post request
|
||||||
|
if self.res and self.res.status_code:
|
||||||
|
return (self.res.status_code == requests.codes.ok)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to obtain the HTTP status with the post_status_ok() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# HTTP GET 1
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# http = HTTP("http://www.google.com")
|
||||||
|
# data = http.get()
|
||||||
|
# print(data)
|
||||||
|
# from Naked.toolshed.file import FileWriter
|
||||||
|
# w = FileWriter("testfile.txt")
|
||||||
|
# w.write_utf8(data)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# HTTP GET 2
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# http = HTTP()
|
||||||
|
# http.url = "http://www.google.com"
|
||||||
|
# print(http.get())
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# cython: profile=True
|
||||||
|
|
||||||
|
from sys import version_info
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Python Versions
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ py_version function ] (tuple of (major, minor, patch))
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def py_version():
|
||||||
|
return (version_info[0], version_info[1], version_info[2])
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ py_major_version function ] (integer)
|
||||||
|
# Return Python interpreter major version number
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def py_major_version():
|
||||||
|
return (version_info[0])
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ py_minor_version function ] (integer)
|
||||||
|
# Return Python interpreter minor version number
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def py_minor_version():
|
||||||
|
return (version_info[1])
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ py_patch_version function ] (integer)
|
||||||
|
# Return Python interpreter patch version number
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def py_patch_version():
|
||||||
|
return (version_info[2])
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_py2 function ] (boolean)
|
||||||
|
# Return truth result for question is interpreter running a version of Python 2
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_py2():
|
||||||
|
return (version_info[0] == (2))
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_py3 function ] (boolean)
|
||||||
|
# Return truth result for question is interpreter running a version of Python 3
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_py3():
|
||||||
|
return (version_info[0] == (3))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,18 @@
|
|||||||
|
from distutils.core import setup
|
||||||
|
from distutils.extension import Extension
|
||||||
|
|
||||||
|
ext_bench = Extension("benchmarking", ["benchmarking.c"])
|
||||||
|
ext_casts = Extension("casts", ["casts.c"])
|
||||||
|
ext_file = Extension("file", ["file.c"])
|
||||||
|
ext_ink = Extension("ink", ["ink.c"])
|
||||||
|
ext_net = Extension("network", ["network.c"])
|
||||||
|
ext_py = Extension("python", ["python.c"])
|
||||||
|
ext_shell = Extension("shell", ["shell.c"])
|
||||||
|
ext_state = Extension("cstate", ["cstate.c"])
|
||||||
|
ext_sys = Extension("system", ["system.c"])
|
||||||
|
ext_types = Extension("types", ["types.c"])
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
ext_modules = [ext_bench, ext_casts, ext_file, ext_ink, ext_net, ext_py, ext_shell, ext_state, ext_sys, ext_types]
|
||||||
|
)
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,264 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# cython: profile=False
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ execute function ] (boolean)
|
||||||
|
# run a shell command and print std out / std err to terminal
|
||||||
|
# returns True if exit status = 0
|
||||||
|
# returns False if exit status != 0
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def execute(command):
|
||||||
|
try:
|
||||||
|
response = subprocess.call(command, shell=True)
|
||||||
|
if response == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except subprocess.CalledProcessError as cpe:
|
||||||
|
try:
|
||||||
|
sys.stderr.write(cpe.output)
|
||||||
|
except TypeError as te:
|
||||||
|
sys.stderr.write(str(cpe.output))
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the execute() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ run function ] (bytes string or False)
|
||||||
|
# run a shell command
|
||||||
|
# default =
|
||||||
|
# success:: print to std out and return the std out string
|
||||||
|
# error:: print to stderr return False, suppress SystemExit on error to permit ongoing run of calling script
|
||||||
|
# suppress_stdout = True >> suppress std output stream print (returns string)
|
||||||
|
# suppress_stderr = True >> suppress std err stream print (returns False)
|
||||||
|
# suppress_exit_status_call = False >> raise SystemExit with the returned status code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def run(command, suppress_stdout=False, suppress_stderr=False, suppress_exit_status_call=True):
|
||||||
|
try:
|
||||||
|
response = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
|
||||||
|
if not suppress_stdout:
|
||||||
|
print(response)
|
||||||
|
return response
|
||||||
|
except subprocess.CalledProcessError as cpe:
|
||||||
|
if not suppress_stderr: # error in existing application (non-zero exit status)
|
||||||
|
try:
|
||||||
|
sys.stderr.write(cpe.output)
|
||||||
|
except TypeError as te: # deal with unusual errors from some system executables that return non string type through subprocess.check_output
|
||||||
|
sys.stderr.write(str(cpe.output))
|
||||||
|
if not suppress_exit_status_call:
|
||||||
|
if cpe.returncode:
|
||||||
|
sys.exit(cpe.returncode)
|
||||||
|
else:
|
||||||
|
sys.exit(1)
|
||||||
|
return False # return False on non-zero exit status codes (i.e. failures in the subprocess executable)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the run() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ muterun function ] (NakedObject with attributes for stdout, stderr, exitcode)
|
||||||
|
# run a shell command and return a response object
|
||||||
|
# return object attributes : stdout (bytes), stderr (bytes), exitcode (int)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def muterun(command):
|
||||||
|
try:
|
||||||
|
from Naked.toolshed.types import NakedObject
|
||||||
|
response_obj = NakedObject()
|
||||||
|
response = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
|
||||||
|
response_obj.stdout = response
|
||||||
|
response_obj.exitcode = 0
|
||||||
|
response_obj.stderr = b""
|
||||||
|
return response_obj
|
||||||
|
except subprocess.CalledProcessError as cpe:
|
||||||
|
response_obj.stdout = b""
|
||||||
|
response_obj.stderr = cpe.output
|
||||||
|
if cpe.returncode:
|
||||||
|
response_obj.exitcode = cpe.returncode
|
||||||
|
else:
|
||||||
|
response_obj.exitcode = 1
|
||||||
|
return response_obj
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the mute_run() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# RUBY COMMAND EXECUTION
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ execute_rb function ] (boolean)
|
||||||
|
# execute a ruby script file in a shell subprocess
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def execute_rb(file_path, arguments=""):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
rb_command = 'ruby ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
rb_command = 'ruby ' + file_path
|
||||||
|
return execute(rb_command) # return result of execute() of the ruby file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the run_rb() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ run_rb function ] (bytes string or False)
|
||||||
|
# execute a ruby script file in a shell subprocess, return the output
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def run_rb(file_path, arguments="", suppress_stdout=False, suppress_stderr=False, suppress_exit_status_call=True):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
rb_command = 'ruby ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
rb_command = 'ruby ' + file_path
|
||||||
|
return run(rb_command, suppress_stdout, suppress_stderr, suppress_exit_status_call) # return result of run() of the ruby file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the run_rb() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ muterun_rb function ] (NakedObject response object)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def muterun_rb(file_path, arguments=""):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
rb_command = 'ruby ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
rb_command = 'ruby ' + file_path
|
||||||
|
return muterun(rb_command) # return result of muterun() of the ruby file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the muterun_rb() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# NODE.JS COMMAND EXECUTION
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ execute_js function ] (boolean)
|
||||||
|
# execute a node.js script file in a shell subprocess
|
||||||
|
# stdout stream to terminal
|
||||||
|
# returns True for success (=0) exit status code
|
||||||
|
# returns False for unsuccessful (!=0) exit status code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def execute_js(file_path, arguments=""):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
js_command = 'node ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
js_command = 'node ' + file_path
|
||||||
|
return execute(js_command) # return result of execute() of node.js file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the run_js() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ run_js function ] (byte string or False)
|
||||||
|
# execute a node.js script file in a shell subprocess
|
||||||
|
# print the standard output to the standard output stream by default
|
||||||
|
# set suppress_output to True to suppress stream to standard output. String is still returned to calling function
|
||||||
|
# set suppress_exit_status_call to True to suppress raising sys.exit on failures with shell subprocess exit status code (if available) or 1 if not available
|
||||||
|
# returns the standard output byte string from the subprocess executable on success
|
||||||
|
# returns False if the subprocess exits with a non-zero exit code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def run_js(file_path, arguments="", suppress_stdout=False, suppress_stderr=False, suppress_exit_status_call=True):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
js_command = 'node ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
js_command = 'node ' + file_path
|
||||||
|
return run(js_command, suppress_stdout, suppress_stderr, suppress_exit_status_call) # return result of run() of node.js file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the run_js() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ muterun_js function ] (NakedObject response object)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def muterun_js(file_path, arguments=""):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
js_command = 'node ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
js_command = 'node ' + file_path
|
||||||
|
return muterun(js_command) # return result of muterun() of node.js file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the muterun_js() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ Environment Class ]
|
||||||
|
# shell environment variables class
|
||||||
|
# self.env = the environment variable dictionary
|
||||||
|
# self.vars = the environment variable names list
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class Environment():
|
||||||
|
def __init__(self):
|
||||||
|
self.env = os.environ
|
||||||
|
self.vars = list(os.environ.keys())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_var method ] (boolean)
|
||||||
|
# return boolean for presence of a variable name in the shell environment
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_var(self, var_name):
|
||||||
|
try:
|
||||||
|
return (var_name in self.vars)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to determine if the variable is included in the shell variable list with the is_var() method (Naked.toolshed.shell).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_var method ] (string)
|
||||||
|
# get the variable value for a variable in the shell environment list
|
||||||
|
# returns empty string if the variable is not included in the environment variable list
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_var(self, var_name):
|
||||||
|
try:
|
||||||
|
if var_name in self.vars:
|
||||||
|
return self.env[var_name]
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return the requested shell variable with the get_var() method (Naked.toolshed.shell).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_split_var_list method ] (list of strings)
|
||||||
|
# return a list of strings split by OS dependent separator from the shell variable assigment string
|
||||||
|
# if the variable name is not in the environment list, returns an empty list
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_split_var_list(self, var_name):
|
||||||
|
try:
|
||||||
|
if var_name in self.vars:
|
||||||
|
return self.env[var_name].split(os.pathsep)
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return environment variable list with the get_split_var_list() method (Naked.toolshed.shell).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
||||||
|
# e = Environment()
|
||||||
|
# pathlist = e.get_split_var_list("PATH")
|
||||||
|
# for item in pathlist:
|
||||||
|
# print(item)
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,554 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# cython: profile=False
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# FILE & DIRECTORY PATHS
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ filename function ] (string)
|
||||||
|
# returns file name from a file path (including the file extension)
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_filename
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def filename(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.basename(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return base filename from filename() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ file_extension function ] (string)
|
||||||
|
# returns file extension from a filepath
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_file_extension
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def file_extension(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.splitext(filepath)[1]
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return file extension with file_extension() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ directory function ] (string)
|
||||||
|
# returns directory path to the filepath
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_dir_path
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def directory(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.dirname(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return directory path to file with directory() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ make_path function ] (string)
|
||||||
|
# returns OS independent file path from tuple of path components
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_make_filepath
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def make_path(*path_list):
|
||||||
|
try:
|
||||||
|
return os.path.join(*path_list)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to make OS independent path with the make_path() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ currentdir_to_basefile decorator function ] (returns decorated original function)
|
||||||
|
# concatenates the absolute working directory path to the basename of file in the first parameter of the undecorated function
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_add_currentdir_path_to_basefile
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def currentdir_to_basefile(func):
|
||||||
|
try:
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(file_name, *args, **kwargs):
|
||||||
|
current_directory = os.getcwd() #get current working directory path
|
||||||
|
full_path = os.path.join(current_directory, file_name) # join cwd path to the filename for full path
|
||||||
|
return func(full_path, *args, **kwargs) #return the original function with the full path to file as first argument
|
||||||
|
return wrapper
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with the currentdir_to_basefile() decorator function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ currentdir_firstparam decorator function ] (returns decorated original function)
|
||||||
|
# adds the current working directory as the first function parameter of the decorated function
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_add_currentdir_path_first_arg
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def currentdir_firstparam(func):
|
||||||
|
try:
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(dir="", *args, **kwargs):
|
||||||
|
current_directory = os.getcwd()
|
||||||
|
return func(current_directory, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with the currentdir_firstargument() decorator function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ currentdir_lastargument decorator function ] (returns decorated original function)
|
||||||
|
# adds the current working directory as the last function parameter of the decorated function
|
||||||
|
# Note: you cannot use other named arguments in the original function with this decorator
|
||||||
|
# Note: the current directory argument in the last position must be named current_dir
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_add_currentdir_last_arg
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def currentdir_lastparam(func):
|
||||||
|
try:
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
the_cwd = os.getcwd()
|
||||||
|
return func(*args, current_dir=the_cwd)
|
||||||
|
return wrapper
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with the currentdir_lastargument() decorator function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ fullpath function ] (string)
|
||||||
|
# returns the absolute path to a file that is in the current working directory
|
||||||
|
# file_name = the basename of the file in the current working directory
|
||||||
|
# Example usage where test.txt is in working directory:
|
||||||
|
# filepath = fullpath("test.txt")
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_full_path_to_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
@currentdir_to_basefile # current directory decorator - adds the directory path up to the filename to the basefile name argument to original function
|
||||||
|
def fullpath(file_name):
|
||||||
|
try:
|
||||||
|
return file_name
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return absolute path to the file with the fullpath() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ cwd function ] (string)
|
||||||
|
# returns the current working directory path
|
||||||
|
# does not need to be called with an argument, the decorator assigns it
|
||||||
|
# Example usage:
|
||||||
|
# current_dir = cwd()
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_cwd_path
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
@currentdir_firstparam
|
||||||
|
def cwd(dir=""):
|
||||||
|
try:
|
||||||
|
return dir
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return the current working directory with the cwd() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# DIRECTORY WRITES
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## TODO: add tests
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ make_dirs function ] (--none--)
|
||||||
|
# make a new directory path (recursive if multiple levels of depth) if it
|
||||||
|
# DOES NOT already exist
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def make_dirs(dirpath):
|
||||||
|
try:
|
||||||
|
import os
|
||||||
|
import errno
|
||||||
|
os.makedirs(dirpath)
|
||||||
|
return True
|
||||||
|
except OSError as ose:
|
||||||
|
if ose.errno != errno.EEXIST: # directory already exists
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Could not write the directory path passed as an argument to the make_dirs() function (Naked.toolshed.system).")
|
||||||
|
raise ose
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# FILE & DIRECTORY TESTING
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ file_exists function ] (boolean)
|
||||||
|
# return boolean for existence of file in specified path
|
||||||
|
# Tests: test_SYSTEM.py :: test_file_exists
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def file_exists(filepath):
|
||||||
|
try:
|
||||||
|
if os.path.exists(filepath) and os.path.isfile(filepath): # test that exists and is a file
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with test for the presence of the file with the file_exists() method (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_file function ] (boolean)
|
||||||
|
# returns boolean for determination of whether filepath is a file
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_is_file, test_sys_is_file_missing_file,
|
||||||
|
# test_sys_is_file_when_dir
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_file(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.isfile(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with test for file with the is_file() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ dir_exists function ] (boolean)
|
||||||
|
# return boolean for existence of directory in specified path
|
||||||
|
# Tests: test_SYSTEM.py :: test_dir_exists, test_dir_exists_missing_dir
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def dir_exists(dirpath):
|
||||||
|
try:
|
||||||
|
if os.path.exists(dirpath) and os.path.isdir(dirpath): # test that exists and is a directory
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with test for directory with the dir_exists() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_dir function ] (boolean)
|
||||||
|
# returns boolean for determination of whether dirpath is a directory
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_dir_is_dir, test_sys_dir_is_dir_when_file,
|
||||||
|
# test_sys_dir_is_dir_when_missing
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_dir(dirpath):
|
||||||
|
try:
|
||||||
|
return os.path.isdir(dirpath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with test for directory with the is_dir() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# FILE METADATA
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ filesize function ] (int)
|
||||||
|
# return file size in bytes
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_meta_file_size
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def file_size(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.getsize(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return file size with the file_size() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ file_mod_time function ] (string)
|
||||||
|
# return the last file modification date/time
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_meta_file_mod
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def file_mod_time(filepath):
|
||||||
|
try:
|
||||||
|
import time
|
||||||
|
return time.ctime(os.path.getmtime(filepath))
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return file modification data with the file_mod_time() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# FILE LISTINGS
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ list_all_files function ] (list)
|
||||||
|
# returns a list of all files in developer specified directory
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_list_all_files, test_sys_list_all_files_emptydir
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def list_all_files(dir):
|
||||||
|
try:
|
||||||
|
filenames = [name for name in os.listdir(dir) if os.path.isfile(os.path.join(dir, name))]
|
||||||
|
return filenames
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to generate directory file list with the list_all_files() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ list_filter_files function ] (list)
|
||||||
|
# returns a list of files filtered by developer defined file extension in developer defined directory
|
||||||
|
# Usage example:
|
||||||
|
# filenames = list_filter_files("py", "tests")
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_list_filter_files, test_sys_list_filter_files_nomatch
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def list_filter_files(extension_filter, dir):
|
||||||
|
try:
|
||||||
|
if not extension_filter.startswith("."):
|
||||||
|
extension_filter = "." + extension_filter
|
||||||
|
filenames = [name for name in os.listdir(dir) if name.endswith(extension_filter)]
|
||||||
|
return filenames
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return list of filtered files with the list_filter_files() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ list_all_files_cwd function ] (list)
|
||||||
|
# returns a list of all files in the current working directory
|
||||||
|
# Note: does not require argument, the decorator assigns the cwd
|
||||||
|
# Usage example:
|
||||||
|
# file_list = list_all_files_cwd()
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_list_all_files_cwd
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
@currentdir_firstparam
|
||||||
|
def list_all_files_cwd(dir=""):
|
||||||
|
try:
|
||||||
|
return list_all_files(dir)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return list of all files in current working directory with the list_all_files_cwd() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ list_filter_files_cwd function ] (list)
|
||||||
|
# returns a list of all files in the current working directory filtered by developer specified file extension
|
||||||
|
# Note: do not specify the second argument, decorator assigns it
|
||||||
|
# Usage example:
|
||||||
|
# file_list = list_filter_files_cwd(".py")
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_filter_files_cwd, test_sys_filter_files_cwd_nomatch
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
@currentdir_lastparam
|
||||||
|
def list_filter_files_cwd(extension_filter, current_dir=""):
|
||||||
|
try:
|
||||||
|
return list_filter_files(extension_filter, current_dir)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return list of filtered files in current working directory with the list_filter_files_cwd() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ list_match_files function ] (list)
|
||||||
|
# returns a list of all files that match the developer specified wildcard match pattern
|
||||||
|
# can optionally specify return of full path to the files (rather than relative path from cwd) by setting full_path to True
|
||||||
|
# Usage examples:
|
||||||
|
# file_list = list_match_files("*.py")
|
||||||
|
# file_list_fullpath = list_match_files("*.py", True)
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_match_files, test_sys_match_files_fullpath
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def list_match_files(match_pattern, full_path = False):
|
||||||
|
try:
|
||||||
|
from glob import glob
|
||||||
|
filenames = glob(match_pattern)
|
||||||
|
if full_path:
|
||||||
|
filenames_fullpath = []
|
||||||
|
cwd = os.getcwd()
|
||||||
|
for name in filenames:
|
||||||
|
name = os.path.join(cwd, name) #make the full path to the file
|
||||||
|
filenames_fullpath.append(name) #add to the new list
|
||||||
|
return filenames_fullpath #then return that list
|
||||||
|
else:
|
||||||
|
return filenames
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return list of matched files with the list_match_files() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# SYMBOLIC LINK TESTING
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_link function ] (boolean)
|
||||||
|
# return boolean indicating whether the path is a symbolic link
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_link(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.islink(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to determine whether path is a symbolic link with the is_link() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ real_path function ] (string)
|
||||||
|
# return the real file path pointed to by a symbolic link
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def real_path(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.realpath(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return real path for symbolic link with the real_path() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# DATA STREAMS
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stdout function ]
|
||||||
|
# print to std output stream
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stdout(text):
|
||||||
|
try:
|
||||||
|
print(text)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard output stream with the stdout() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stdout_xnl function ]
|
||||||
|
# print to std output stream without a newline
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stdout_xnl(text):
|
||||||
|
try:
|
||||||
|
sys.stdout.write(text)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard output stream with the stdout_xnl() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stdout_iter function ]
|
||||||
|
# print items in an iterable to the standard output stream with newlines after each string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stdout_iter(iter):
|
||||||
|
try:
|
||||||
|
for x in iter:
|
||||||
|
stdout(x)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard output stream with the stdout_iter() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stdout_iter_xnl function ]
|
||||||
|
# print items in an iterable to the standard output stream without newlines after each string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stdout_iter_xnl(iter):
|
||||||
|
try:
|
||||||
|
for x in iter:
|
||||||
|
stdout_xnl(x)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard output stream with the stdout_iter() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stderr function ]
|
||||||
|
# print to std error stream
|
||||||
|
# optionally (i.e. if exit = nonzero integer) permits exit from application with developer defined exit code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stderr(text, exit=0):
|
||||||
|
try:
|
||||||
|
sys.stderr.write(text + "\n")
|
||||||
|
if exit:
|
||||||
|
raise SystemExit(exit)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard error stream with the stderr() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stderr_xnl function ]
|
||||||
|
# print to the standard error stream without a newline character after the `text` string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stderr_xnl(text, exit=0):
|
||||||
|
try:
|
||||||
|
sys.stderr.write(text)
|
||||||
|
if exit:
|
||||||
|
raise SystemExit(exit)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard error stream with the stderr() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# APPLICATION CONTROL
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ exit_with_status function ]
|
||||||
|
# application exit with developer specified exit status code (default = 0)
|
||||||
|
# use an exit status integer argument
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_exit_with_code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def exit_with_status(exit=0):
|
||||||
|
raise SystemExit(exit)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ exit_fail function ]
|
||||||
|
# application exit with status code 1
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_exit_failure
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def exit_fail():
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ exit_success function]
|
||||||
|
# application exit with status code 0
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_exit_success
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def exit_success():
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
||||||
|
# #------------------------------------------------------------------------------
|
||||||
|
# # Standard Output Tests
|
||||||
|
# #------------------------------------------------------------------------------
|
||||||
|
# stdout("This is a test")
|
||||||
|
# for x in range(10):
|
||||||
|
# stdout_xnl(str(x) + " ")
|
||||||
|
# list_ten = ['10% ', '20% ', '30% ', '40% ', '50% ', '60% ', '70% ', '80% ', '90% ', '100%']
|
||||||
|
# stdout_iter(list_ten)
|
||||||
|
# #------------------------------------------------------------------------------
|
||||||
|
# # Standard Error Tests
|
||||||
|
# #------------------------------------------------------------------------------
|
||||||
|
# stderr("This is a test")
|
||||||
|
# stderr("This is a test", 1) #exit with status code 1
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,999 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# cython: profile=False
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ NakedObject class ]]
|
||||||
|
# A generic Python object
|
||||||
|
# Assigns object attributes by key name in the dictionary argument to the constructor
|
||||||
|
# The methods are inherited by other mutable Naked object extension types
|
||||||
|
# Attribute accessors: hasattr, getattr, setattr, delattr
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class NakedObject(object):
|
||||||
|
# initialize with an attributes dictionary {attribute_name: attribute_value}
|
||||||
|
def __init__(self, attributes={}, naked_type='NakedObject'):
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(self, key, attributes[key])
|
||||||
|
setattr(self, '_naked_type_', naked_type) # maintain an attribute to keep track of the extension type
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _getAttributeDict method ] (dictionary)
|
||||||
|
# returns a dictionary of the NakedObject instance attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _getAttributeDict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _equal_type method ] (boolean)
|
||||||
|
# returns boolean for type of instance == type of test parameter instance
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _equal_type(self, other_obj):
|
||||||
|
return type(self) == type(other_obj)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _equal_attributes metod ] (method)
|
||||||
|
# returns boolean for instance.__dict__ == test parameter .__dict__ (attribute comparison)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _equal_attributes(self, other_obj):
|
||||||
|
return self.__dict__ == other_obj.__dict__
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# == overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __eq__(self, other_obj):
|
||||||
|
return self.equals(other_obj)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# != overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __ne__(self, other_obj):
|
||||||
|
result = self.equals(other_obj)
|
||||||
|
if result:
|
||||||
|
return False # reverse result of the equals method
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ equals method ] (boolean)
|
||||||
|
# equality testing based on type and attributes
|
||||||
|
# **NEED TO OVERRIDE IN CLASSES THAT INHERIT
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def equals(self, other_obj):
|
||||||
|
return self._equal_type(other_obj) and self._equal_attributes(other_obj)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ type method ] (string)
|
||||||
|
# returns the Naked type extension string that is set in the constructor for each object type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def type(self):
|
||||||
|
if hasattr(self, '_naked_type_'):
|
||||||
|
return self._naked_type_
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XDict class ]]
|
||||||
|
# An inherited extension to the dictionary type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XDict(dict, NakedObject):
|
||||||
|
def __init__(self, dict_obj, attributes={}, naked_type='XDict'):
|
||||||
|
dict.__init__(self, dict_obj)
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XDict Operator Overloads
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# + overload
|
||||||
|
# overwrites existing keys with key:value pairs from new dictionaries if they are the same keys
|
||||||
|
# returns the updated XDict object
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __add__(self, other_dict):
|
||||||
|
try:
|
||||||
|
self.update(other_dict)
|
||||||
|
if hasattr(other_dict, '_naked_type_') and (getattr(other_dict, '_naked_type_') == 'XDict'):
|
||||||
|
attr_dict = other_dict._getAttributeDict() # get the attributes from the parameter XDict and add to new XDict
|
||||||
|
if len(attr_dict) > 0:
|
||||||
|
for key in attr_dict:
|
||||||
|
setattr(self, key, attr_dict[key])
|
||||||
|
return self
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to combine XDict with parameter provided (Naked.toolshed.types.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# +- overload
|
||||||
|
# overwrites existing keys with another_dict (right sided argument) keys if they are the same keys
|
||||||
|
# returns the updated XDict object
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __iadd__(self, other_dict):
|
||||||
|
try:
|
||||||
|
self.update(other_dict)
|
||||||
|
if hasattr(other_dict, '_naked_type_') and (getattr(other_dict, '_naked_type_') == 'XDict'):
|
||||||
|
attr_dict = other_dict._getAttributeDict() # get the attributes from the parameter XDict and add to new XDict
|
||||||
|
if len(attr_dict) > 0:
|
||||||
|
for key in attr_dict:
|
||||||
|
setattr(self, key, attr_dict[key])
|
||||||
|
return self
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to combine XDict with parameter provided (Naked.toolshed.types.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# == overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __eq__(self, other_obj):
|
||||||
|
return self.equals(other_obj)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# != overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __ne__(self, other_obj):
|
||||||
|
result = self.equals(other_obj)
|
||||||
|
if result:
|
||||||
|
return False # reverse result of the equals method
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ equals method ] (boolean)
|
||||||
|
# tests for equality of the XDict (type, attributes, dictionary equality)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def equals(self, other_obj):
|
||||||
|
if self._equal_type(other_obj) and self._equal_attributes(other_obj):
|
||||||
|
if dict(self) == dict(other_obj):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XDict Value Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ conditional_map_to_vals method ] (XDict)
|
||||||
|
# returns the original XDict with values that meet True condition in `conditional_function`
|
||||||
|
# modified as per the `mapped_function` with single value argument call
|
||||||
|
# Test: test_xdict_conditional_map
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def conditional_map_to_vals(self, conditional_function, mapped_function):
|
||||||
|
for key, value in self.xitems():
|
||||||
|
if conditional_function(key):
|
||||||
|
self[key] = mapped_function(value)
|
||||||
|
return self
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ map_to_vals method ] (XDict)
|
||||||
|
# returns the original XDict with all values modified as per the `mapped_function`
|
||||||
|
# Test: test_xdict_map_to_vals
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def map_to_vals(self, mapped_function):
|
||||||
|
# return XDict( zip(self, map(mapped_function, self.values())), self._getAttributeDict() ) - slower in Py2
|
||||||
|
for key, value in self.xitems():
|
||||||
|
self[key] = mapped_function(value)
|
||||||
|
return self
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ val_xlist method ] (XList)
|
||||||
|
# return an XList of the values in the XDict
|
||||||
|
# Test: test_xdict_val_xlist
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def val_xlist(self):
|
||||||
|
return XList(self.values(), self._getAttributeDict())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ max_val method ] (tuple of maximum value and associated key)
|
||||||
|
# Test: test_xdict_max_val, test_xdict_max_val_strings (strings are alphabetic if not numerals)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def max_val(self):
|
||||||
|
return max(zip(self.values(), self.keys()))
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ min_val method ] (tuple of minimum value and associated key)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def min_val(self):
|
||||||
|
return min(zip(self.values(), self.keys()))
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ sum_vals method ] (numeric return type dependent upon original value type)
|
||||||
|
# returns sum of all values in the dictionary
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def sum_vals(self):
|
||||||
|
return sum(self.values())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ val_count method ] (integer)
|
||||||
|
# returns an integer value for the total count of `value_name` in the dictionary values
|
||||||
|
# Case sensitive test if comparing strings
|
||||||
|
# Tests: test_xdict_val_count_string, test_xdict_val_count_integer
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def val_count(self, value_name):
|
||||||
|
count = 0
|
||||||
|
for test_value in self.values():
|
||||||
|
if value_name == test_value:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ value_count_ci method ] (integer)
|
||||||
|
# returns an integer value for the total count of case insensitive `value_name`
|
||||||
|
# strings/char in the dictionary values. Can include non-string types (ignores them)
|
||||||
|
# Test: test_xdict_val_count_ci
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def val_count_ci(self, value_name):
|
||||||
|
count = 0
|
||||||
|
for test_value in self.values():
|
||||||
|
try:
|
||||||
|
if value_name.lower() in test_value.lower():
|
||||||
|
count += 1
|
||||||
|
except AttributeError: # the test_value was not a string, catch exception and continue count attempt
|
||||||
|
continue
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XDict Key Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ difference method ] (difference set of keys)
|
||||||
|
# definition: keys that are included in self, but not in `another_dict`
|
||||||
|
# Tests: test_xdict_key_difference, test_xdict_key_difference_when_none_present
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def difference(self, another_dict):
|
||||||
|
return set(self.keys()) - set(another_dict.keys())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ intersection method ] (intersection set of keys)
|
||||||
|
# definition: keys that are included in both self and `another_dict`
|
||||||
|
# Tests: test_xdict_key_intersection, test_xdict_key_intersection_when_none_present
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def intersection(self, another_dict):
|
||||||
|
return set(self.keys()) & set(another_dict.keys())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ key_xlist method ] (XList)
|
||||||
|
# returns an XList of the keys in the XDict
|
||||||
|
# Test: test_xdict_key_xlist
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def key_xlist(self):
|
||||||
|
return XList(self.keys(), self._getAttributeDict())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ random method ] (dictionary)
|
||||||
|
# return new Python dictionary with single, random key:value pair
|
||||||
|
# Test: test_xdict_key_random
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def random(self):
|
||||||
|
import random
|
||||||
|
from Naked.toolshed.python import py_major_version
|
||||||
|
random_key_list = random.sample(self.keys(), 1)
|
||||||
|
the_key = random_key_list[0]
|
||||||
|
return {the_key: self[the_key]}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ random_sample method ] (dictionary)
|
||||||
|
# return new Python dictionary with `number_of_items` random key:value pairs
|
||||||
|
# Test: test_xdict_key_random_sample
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def random_sample(self, number_of_items):
|
||||||
|
import random
|
||||||
|
random_key_list = random.sample(self.keys(), number_of_items)
|
||||||
|
new_dict = {}
|
||||||
|
for item in random_key_list:
|
||||||
|
new_dict[item] = self[item]
|
||||||
|
return new_dict
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xitems method ] (tuple)
|
||||||
|
# Generator method that returns tuples of every key, value in dictionary
|
||||||
|
# uses appropriate method from Python 2 and 3 interpreters
|
||||||
|
# Test: test_xdict_xitems
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xitems(self):
|
||||||
|
from Naked.toolshed.python import py_major_version
|
||||||
|
if py_major_version() > 2:
|
||||||
|
return self.items()
|
||||||
|
else:
|
||||||
|
return self.iteritems()
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XList class ]]
|
||||||
|
# An inherited extension to the list object that permits attachment of attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XList(list, NakedObject):
|
||||||
|
def __init__(self, list_obj, attributes={}, naked_type='XList'):
|
||||||
|
list.__init__(self, list_obj)
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Operator Overloads
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# + operator overload
|
||||||
|
# extends XList with one or more other lists (`*other_lists`)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __add__(self, *other_lists):
|
||||||
|
try:
|
||||||
|
for the_list in other_lists:
|
||||||
|
# add attributes if it is an XList
|
||||||
|
if hasattr(the_list, '_naked_type_') and (getattr(the_list, '_naked_type_') == 'XList'):
|
||||||
|
attr_dict = the_list._getAttributeDict() # get XList attribute dictionary
|
||||||
|
if len(attr_dict) > 0:
|
||||||
|
for key in attr_dict:
|
||||||
|
setattr(self, key, attr_dict[key])
|
||||||
|
# extend the XList items
|
||||||
|
self.extend(the_list)
|
||||||
|
return self
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to combine XList with parameter provided (Naked.toolshed.types.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# += overload
|
||||||
|
# extends XList with one other list (`another_list`)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __iadd__(self, another_list):
|
||||||
|
try:
|
||||||
|
#add attributes if it is an XList
|
||||||
|
if hasattr(another_list, '_naked_type_') and (getattr(another_list, '_naked_type_') == 'XList'):
|
||||||
|
attr_dict = another_list._getAttributeDict() # get XList attribute dictionary
|
||||||
|
if len(attr_dict) > 0:
|
||||||
|
for key in attr_dict:
|
||||||
|
setattr(self, key, attr_dict[key])
|
||||||
|
# extend the XList items
|
||||||
|
self.extend(another_list)
|
||||||
|
return self
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to combine XList with parameter provided (Naked.toolshed.types.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# == overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __eq__(self, other_obj):
|
||||||
|
return self.equals(other_obj)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# != overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __ne__(self, other_obj):
|
||||||
|
result = self.equals(other_obj)
|
||||||
|
if result:
|
||||||
|
return False # reverse result of the equals method
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ equals method ] (boolean)
|
||||||
|
# tests for equality of the XList (type, attributes, list equality)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def equals(self, other_obj):
|
||||||
|
if self._equal_type(other_obj) and self._equal_attributes(other_obj):
|
||||||
|
if list(self) == list(other_obj):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList String Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ join method ] (string)
|
||||||
|
# Concatenate strings in the list and return
|
||||||
|
# Default separator between string list values is an empty string
|
||||||
|
# Pass separator character(s) as an argument to the method
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def join(self, separator=""):
|
||||||
|
return separator.join(self)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ postfix method ] (list of strings)
|
||||||
|
# Append a string to each list item string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def postfix(self, after):
|
||||||
|
return [ "".join([x, after]) for x in self ]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ prefix method ] (list of strings)
|
||||||
|
# Prepend a string to each list item string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def prefix(self, before):
|
||||||
|
return [ "".join([before, x]) for x in self ]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ surround method ] (list of strings)
|
||||||
|
# Surround each list item string with a before and after string argument passed to the method
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def surround(self, before, after=""):
|
||||||
|
if after == "":
|
||||||
|
after = before
|
||||||
|
return [ "".join([before, x, after]) for x in self ]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Numeric Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ max method ] (list dependent type, single value)
|
||||||
|
# return maximum value from the list items
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def max(self):
|
||||||
|
return max(self)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ min method ] (list dependent type, single value)
|
||||||
|
# return minimum value from the list items
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def min(self):
|
||||||
|
return min(self)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ sum method ] (list dependent type, single value)
|
||||||
|
# return the sum of all list items
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def sum(self):
|
||||||
|
return sum(self)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Data Management Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ count_duplicates method ] (integer)
|
||||||
|
# returns an integer count of number of duplicate values
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def count_duplicates(self):
|
||||||
|
return len(self) - len(set(self))
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ remove_duplicates ] (XList)
|
||||||
|
# returns a new XList with duplicates removed
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def remove_duplicates(self):
|
||||||
|
return XList( set(self), self._getAttributeDict() )
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ difference method ] (set)
|
||||||
|
# returns a set containing items in XList that are not contained in `another_list`
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def difference(self, another_list):
|
||||||
|
return set(self) - set(another_list)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ intersection method ] (set)
|
||||||
|
# returns a set containing items that are in both XList and `another_list`
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def intersection(self, another_list):
|
||||||
|
return set(self) & set(another_list)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Function Mapping Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ map_to_items method ] (XList)
|
||||||
|
# returns original XList with modification of each item based upon `mapped_function`
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def map_to_items(self, mapped_function):
|
||||||
|
# return XList( map(mapped_function, self), self._getAttributeDict() ) - slower
|
||||||
|
for index, item in enumerate(self):
|
||||||
|
self[index] = mapped_function(item)
|
||||||
|
return self
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ conditional_map_to_items method ] (XList)
|
||||||
|
# returns original XList with modification of items that meet True condition in
|
||||||
|
# `conditional_function` with change performed as defined in `mapped_function`
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def conditional_map_to_items(self, conditional_function, mapped_function):
|
||||||
|
for index, item in enumerate(self):
|
||||||
|
if conditional_function(item):
|
||||||
|
self[index] = mapped_function(item)
|
||||||
|
return self
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Descriptive Stats Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ count_ci method ] (integer)
|
||||||
|
# returns an integer count of the number of case-insensitive items that match `test_string`
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def count_ci(self, test_string):
|
||||||
|
count = 0
|
||||||
|
for item in self:
|
||||||
|
try:
|
||||||
|
if test_string.lower() in item.lower():
|
||||||
|
count += 1
|
||||||
|
except AttributeError: # the test_value was not a string, catch exception and continue count attempt
|
||||||
|
continue
|
||||||
|
return count
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ random method ] (list)
|
||||||
|
# returns a single item list with a random element from the original XList
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def random(self):
|
||||||
|
import random
|
||||||
|
return random.choice(self)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ random_sample method ] (list)
|
||||||
|
# returns a list with one or more random items from the original XList
|
||||||
|
# number of items determined by the `number_of_items` argument
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def random_sample(self, number_of_items):
|
||||||
|
import random
|
||||||
|
return random.sample(self, number_of_items)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ shuffle method ] (XList)
|
||||||
|
# randomly shuffle the contents of the list
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def shuffle(self):
|
||||||
|
import random
|
||||||
|
random.shuffle(self)
|
||||||
|
return self
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Match Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ wildcard_match method ] (list)
|
||||||
|
# returns a list of items that match the `wildcard` argument
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def wildcard_match(self, wildcard):
|
||||||
|
if hasattr(self, 'nkd_fnmatchcase'):
|
||||||
|
fnmatchcase = self.nkd_fnmatchcase
|
||||||
|
else:
|
||||||
|
from fnmatch import fnmatchcase
|
||||||
|
self.nkd_fnmatchcase = fnmatchcase
|
||||||
|
return [ x for x in self if fnmatchcase(x, wildcard) ]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ multi_wildcard_match method ] (list)
|
||||||
|
# returns a list of items that match one or more | separated wildcards passed as string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def multi_wildcard_match(self, wildcards):
|
||||||
|
if hasattr(self, 'nkd_fnmatchcase'):
|
||||||
|
fnmatchcase = self.nkd_fnmatchcase
|
||||||
|
else:
|
||||||
|
from fnmatch import fnmatchcase
|
||||||
|
self.nkd_fnmatchcase = fnmatchcase
|
||||||
|
wc_list = wildcards.split('|')
|
||||||
|
return_list = []
|
||||||
|
for wc in wc_list:
|
||||||
|
temp_list = [ x for x in self if fnmatchcase(x, wc) ]
|
||||||
|
for result in temp_list:
|
||||||
|
return_list.append(result)
|
||||||
|
return return_list
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Cast Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xset method ] (XSet)
|
||||||
|
# return an XSet with unique XList item values and XList attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xset(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XSet(set(self), attr_dict)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xfset method ] (XFSet)
|
||||||
|
# return an XFSet with unique XList item values and XList attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xfset(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XFSet(set(self), attr_dict)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xtuple method ] (XTuple)
|
||||||
|
# returns an XTuple with XList item values and XList attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xtuple(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XTuple(tuple(self), attr_dict)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XMaxHeap class ]]
|
||||||
|
# max heap queue
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
from heapq import heappush, heappop
|
||||||
|
class XMaxHeap(NakedObject):
|
||||||
|
def __init__(self, attributes={}, naked_type='XMaxHeap'):
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
self._queue = []
|
||||||
|
self._index = 0
|
||||||
|
|
||||||
|
# length of the queue
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
# O(log n) complexity
|
||||||
|
def push(self, the_object, priority):
|
||||||
|
heappush(self._queue, (-priority, self._index, the_object))
|
||||||
|
self._index += 1
|
||||||
|
|
||||||
|
# O(log n) complexity
|
||||||
|
def pop(self):
|
||||||
|
if self._queue:
|
||||||
|
return heappop(self._queue)[-1]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# push new object and return the highest priority object
|
||||||
|
def pushpop(self, the_object, priority):
|
||||||
|
heappush(self._queue, (-priority, self._index, the_object))
|
||||||
|
self._index += 1
|
||||||
|
if self._queue:
|
||||||
|
return heappop(self._queue)[-1]
|
||||||
|
else:
|
||||||
|
return None # return None if the queue is empty
|
||||||
|
|
||||||
|
# the length of the queue
|
||||||
|
def length(self):
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XMinHeap class ]]
|
||||||
|
# min heap queue
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
from heapq import heappush, heappop
|
||||||
|
class XMinHeap(NakedObject):
|
||||||
|
def __init__(self, attributes={}, naked_type='XMinHeap'):
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
self._queue = []
|
||||||
|
self._index = 0
|
||||||
|
|
||||||
|
|
||||||
|
# length of the queue
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
# O(log n) complexity
|
||||||
|
def push(self, the_object, priority):
|
||||||
|
heappush(self._queue, (priority, self._index, the_object))
|
||||||
|
self._index += 1
|
||||||
|
|
||||||
|
# O(log n) complexity
|
||||||
|
def pop(self):
|
||||||
|
if self._queue:
|
||||||
|
return heappop(self._queue)[-1]
|
||||||
|
else:
|
||||||
|
return None # return None if the queue is empty
|
||||||
|
|
||||||
|
# push new object and return the lowest priority object
|
||||||
|
def pushpop(self, the_object, priority):
|
||||||
|
heappush(self._queue, (priority, self._index, the_object))
|
||||||
|
self._index += 1
|
||||||
|
if self._queue:
|
||||||
|
return heappop(self._queue)[-1]
|
||||||
|
else:
|
||||||
|
return None #return None if the queue is empty
|
||||||
|
|
||||||
|
# the length of the queue
|
||||||
|
def length(self):
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XQueue class ]]
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
from collections import deque
|
||||||
|
class XQueue(deque, NakedObject):
|
||||||
|
def __init__(self, initial_iterable=[], attributes={}, max_length=10, naked_type='XQueue'):
|
||||||
|
deque.__init__(self, initial_iterable, max_length)
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XSet class ]]
|
||||||
|
# An inherited extension to the mutable set object that permits attribute assignment
|
||||||
|
# Inherits from set and from NakedObject (see methods in NakedObject at top of this module
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XSet(set, NakedObject):
|
||||||
|
def __init__(self, set_obj, attributes={}, naked_type='XSet'):
|
||||||
|
set.__init__(self, set_obj)
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
|
||||||
|
# << operator is overloaded to extend the XSet with a second set
|
||||||
|
def __lshift__(self, another_set):
|
||||||
|
self.update(another_set)
|
||||||
|
return self
|
||||||
|
|
||||||
|
# += operator overload to extend the XSet with a second set
|
||||||
|
def __iadd__(self, another_set):
|
||||||
|
self.update(another_set)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def xlist(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XList(list(self), attr_dict)
|
||||||
|
|
||||||
|
def xfset(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XFSet(self, attr_dict)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XFSet class ]]
|
||||||
|
# An inherited extension to the immutable frozenset object that permits attribute assignment
|
||||||
|
# Immutable so there is no setter method, attributes must be set in the constructor
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XFSet(frozenset):
|
||||||
|
def __new__(cls, the_set, attributes={}, naked_type="XFSet"):
|
||||||
|
set_obj = frozenset.__new__(cls, the_set)
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(set_obj, key, attributes[key])
|
||||||
|
setattr(set_obj, '_naked_type_', naked_type) # set the naked extension type as an attribute (NakedObject does this for mutable classes)
|
||||||
|
return set_obj
|
||||||
|
|
||||||
|
def _getAttributeDict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
def xlist(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XList(list(self), attr_dict, naked_type="XList")
|
||||||
|
|
||||||
|
def xset(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XSet(self, attr_dict, naked_type="XSet")
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ type method ] (string)
|
||||||
|
# returns the Naked type extension string that is set in the constructor for each object type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def type(self):
|
||||||
|
if hasattr(self, '_naked_type_'):
|
||||||
|
return self._naked_type_
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XString class ]]
|
||||||
|
# An inherited extension to the immutable string object that permits attribute assignment
|
||||||
|
# Immutable so there is no setter method, attributes must be set in the constructor
|
||||||
|
# Python 2: byte string by default, can cast to normalized UTF-8 with XString().unicode() method
|
||||||
|
# Python 3: string (that permits unicode) by default, can normalize with XString().unicode() method
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XString(str):
|
||||||
|
def __new__(cls, string_text, attributes={}, naked_type='XString'):
|
||||||
|
str_obj = str.__new__(cls, string_text)
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(str_obj, key, attributes[key])
|
||||||
|
setattr(str_obj, '_naked_type_', naked_type)
|
||||||
|
return str_obj
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _getAttributeDict method ] (dictionary)
|
||||||
|
# returns a dictionary of the XString instance attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _getAttributeDict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ type method ] (string)
|
||||||
|
# returns the Naked type extension string that is set in the constructor for each object type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def type(self):
|
||||||
|
if hasattr(self, '_naked_type_'):
|
||||||
|
return self._naked_type_
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
## TODO: see where + vs. join breakpoint becomes important
|
||||||
|
def concat(self, *strings):
|
||||||
|
str_list = []
|
||||||
|
for x in strings:
|
||||||
|
str_list.append(x)
|
||||||
|
return "".join(str_list)
|
||||||
|
|
||||||
|
# fastest substring search truth test
|
||||||
|
def contains(self, substring):
|
||||||
|
return substring in self
|
||||||
|
|
||||||
|
# split the string on one or more delimiters, return list
|
||||||
|
# if up to two chars, then uses str.split(), if more chars then use re.split
|
||||||
|
def xsplit(self, split_delimiter):
|
||||||
|
length = len(split_delimiter)
|
||||||
|
if length > 2:
|
||||||
|
import re
|
||||||
|
split_delimiter = "".join([ '[', split_delimiter, ']' ])
|
||||||
|
return re.split(split_delimiter, self)
|
||||||
|
elif length > 1:
|
||||||
|
delim2 = split_delimiter[1]
|
||||||
|
first_list = self.split(split_delimiter[0])
|
||||||
|
result_list = []
|
||||||
|
for item in first_list:
|
||||||
|
for subitem in item.split(delim2):
|
||||||
|
result_list.append(subitem)
|
||||||
|
return result_list
|
||||||
|
else:
|
||||||
|
return self.split(split_delimiter)
|
||||||
|
|
||||||
|
# split the string on one or more characters and return items in set
|
||||||
|
def xsplit_set(self, split_delimiter):
|
||||||
|
return set(self.xsplit(split_delimiter))
|
||||||
|
|
||||||
|
# str begins with substring - faster than str.startswith()
|
||||||
|
def begins(self, begin_string):
|
||||||
|
return begin_string in self[0:len(begin_string)]
|
||||||
|
|
||||||
|
# str ends with substring - faster than str.endswith()
|
||||||
|
def ends(self, end_string):
|
||||||
|
return end_string in self[-len(end_string):]
|
||||||
|
|
||||||
|
# case sensitive wildcard match on the XString (boolean returned)
|
||||||
|
def wildcard_match(self, wildcard):
|
||||||
|
from fnmatch import fnmatchcase
|
||||||
|
return fnmatchcase(self, wildcard)
|
||||||
|
|
||||||
|
# convert string to normalized UTF-8 in Python 2 and 3 (##TODO: convert to XUnicode with attributes?)
|
||||||
|
def unicode(self):
|
||||||
|
from sys import version_info
|
||||||
|
from unicodedata import normalize
|
||||||
|
if version_info[0] == 2:
|
||||||
|
return normalize('NFKD', self.decode('UTF-8'))
|
||||||
|
else:
|
||||||
|
return normalize('NFKD', self)
|
||||||
|
|
||||||
|
|
||||||
|
# this version works
|
||||||
|
class XUnicode:
|
||||||
|
def __init__(self, string_text, attributes={}, naked_type='XUnicode'):
|
||||||
|
import sys
|
||||||
|
import unicodedata
|
||||||
|
norm_text = unicodedata.normalize('NFKD', string_text)
|
||||||
|
|
||||||
|
class XUnicode_2(unicode):
|
||||||
|
def __new__(cls, the_string_text, attributes={}, naked_type='XUnicode2'):
|
||||||
|
str_obj = unicode.__new__(cls, the_string_text)
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(str_obj, key, attributes[key])
|
||||||
|
setattr(str_obj, '_naked_type_', naked_type) # set the type to XUnicode2 for Py 2 strings
|
||||||
|
return str_obj
|
||||||
|
|
||||||
|
class XUnicode_3(str):
|
||||||
|
def __new__(cls, the_string_text, attributes={}, naked_type='XUnicode3'):
|
||||||
|
str_obj = str.__new__(cls, the_string_text)
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(str_obj, key, attributes[key])
|
||||||
|
setattr(str_obj, '_naked_type_', naked_type) # set the type to XUnicode3 for Py 3 strings
|
||||||
|
return str_obj
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info[0] == 2:
|
||||||
|
self.obj = XUnicode_2(norm_text, attributes)
|
||||||
|
self.norm_unicode = norm_text
|
||||||
|
self.naked_u_string = self.obj.encode('utf-8') # utf-8 encoded byte string
|
||||||
|
elif sys.version_info[0] == 3:
|
||||||
|
self.naked_u_string = XUnicode_3(norm_text, attributes).encode('utf-8') # ?
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# return self.naked_u_string
|
||||||
|
return self.obj
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.naked_u_string
|
||||||
|
|
||||||
|
def __getattr__(self, the_attribute):
|
||||||
|
return self.obj.__dict__[the_attribute]
|
||||||
|
|
||||||
|
def __cmp__(self, other_string):
|
||||||
|
return hash(self.naked_u_string) == hash(other_string)
|
||||||
|
# TODO: add check for same attributes
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _getAttributeDict method ] (dictionary)
|
||||||
|
# returns a dictionary of the NakedObject instance attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _getAttributeDict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ type method ] (string)
|
||||||
|
# returns the Naked type extension string that is set in the constructor for each object type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def type(self):
|
||||||
|
if hasattr(self, '_naked_type_'):
|
||||||
|
return self._naked_type_
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XTuple class ]]
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XTuple(tuple):
|
||||||
|
def __new__(cls, the_tuple, attributes={}, naked_type='XTuple'):
|
||||||
|
tup_obj = tuple.__new__(cls, the_tuple)
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(tup_obj, key, attributes[key])
|
||||||
|
setattr(tup_obj, '_naked_type_', naked_type)
|
||||||
|
return tup_obj
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _getAttributeDict method ] (dictionary)
|
||||||
|
# returns a dictionary of the NakedObject instance attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _getAttributeDict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ type method ] (string)
|
||||||
|
# returns the Naked type extension string that is set in the constructor for each object type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def type(self):
|
||||||
|
if hasattr(self, '_naked_type_'):
|
||||||
|
return self._naked_type_
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
||||||
|
# no = nobj({"version":"1.0.1", "test":"code"})
|
||||||
|
# print(no)
|
||||||
|
# print(no.version)
|
||||||
|
# print(no.test)
|
||||||
|
# nl = XList([1, 2, 3, 1, 2, 5], {"version":"1.0.1", "test":"code"})
|
||||||
|
# print(nl.count_duplicates())
|
||||||
|
# the_list = list(range(5000))
|
||||||
|
# nl = XList(the_list)
|
||||||
|
# nq = XPriorityQueue()
|
||||||
|
# nq.push('test', 5)
|
||||||
|
# nq.push('one', 3)
|
||||||
|
# nq.push('another', 4)
|
||||||
|
# print(nq.pop())
|
||||||
|
# print(nq.pop())
|
||||||
|
# print(nq.pop())
|
||||||
|
|
||||||
|
# nl = XList([2, 2, 2, 'another'], {'p': 'attribute'})
|
||||||
|
# print(nl)
|
||||||
|
# print(nl.count_item(2))
|
||||||
|
# nq = XQueue(nl, max_length=2)
|
||||||
|
# print(nq)
|
||||||
|
|
||||||
|
# xs = XSet({'test', 'true', 'false'}, {'bonus': 'candy', 'test': 'another'})
|
||||||
|
# xs += {'bogus', 'yep'}
|
||||||
|
# print(xs)
|
||||||
|
|
||||||
|
# xd = XDict({'test2': 0, 'is': 1}, {'a': '1', 'b': '2'})
|
||||||
|
# ad = {'test': 0, 'is': 2}
|
||||||
|
# ld = xd.intersection(ad)
|
||||||
|
# print(ld)
|
||||||
|
# xd = xd + ad + ld
|
||||||
|
# print(xd.map_to_vals(pr))
|
||||||
|
# print(xd.a)
|
||||||
|
# print(xd)
|
||||||
|
# print(xd.a)
|
||||||
|
# print(xd.min_val())
|
||||||
|
# print(xd.conditional_map_to_vals(matcher, resulter))
|
||||||
|
|
||||||
|
# nl = XList([ 'test.txt', 'bogus.txt', 'test.py', 'another.rb', 'est.doc', 'est.py' ])
|
||||||
|
# print(nl.multi_wildcard_match('*.py|*.txt|*.doc'))
|
||||||
|
|
||||||
|
# xstr = XString("Hey! Cœur It's Bengali ব য,\nand here is some more ২")
|
||||||
|
# ustr = xstr.unicode()
|
||||||
|
# print(isinstance(ustr, bytes))
|
||||||
|
# print(xstr)
|
||||||
|
|
@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# tests in test_CASTS.py
|
||||||
|
|
||||||
|
from Naked.toolshed.types import NakedObject, XFSet, XDict, XList, XQueue, XSet, XString, XTuple
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ nobj ] (NakedObject)
|
||||||
|
# Cast a dictionary of attributes to a NakedObject with key>attribute mapping
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def nobj(attributes={}):
|
||||||
|
try:
|
||||||
|
return NakedObject(attributes)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to create a NakedObject with the requested argument using the nobj() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xd function ] (XDict)
|
||||||
|
# Cast a Python dictionary to a XDict
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xd(dictionary_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XDict(dictionary_arg, attributes)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError("Attempted to cast to a XDict with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XDict with the xd() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xl function ] (XList)
|
||||||
|
# Cast a Python list, set, or tuple to a XList
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xl(list_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XList(list_arg, attributes)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError("Attempted to cast to a XList with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XList with the xl() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xq function ] (XQueue)
|
||||||
|
# Cast a Python list, set, tuple to a XQueue
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xq(queue_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XQueue(queue_arg, attributes)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError("Attempted to cast to a XQueue with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XQueue with the xq() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xset function ] (XSet)
|
||||||
|
# Cast a Python set to a XSet
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xset(set_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XSet(set_arg, attributes)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError("Attempted to cast to a XSet with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XSet with the xset() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xfset function ] (XFSet)
|
||||||
|
# Cast a Python set to a XFSet
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xfset(set_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XFSet(set_arg, attributes)
|
||||||
|
except TypeError:
|
||||||
|
raise TypeError("Attempted to cast to a XSet with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XSet with the xset() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xstr function ] (XString)
|
||||||
|
# Cast a Python string to a XString
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xstr(string_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XString(string_arg, attributes)
|
||||||
|
except TypeError as te:
|
||||||
|
raise TypeError("Attempted to cast to a XString with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XString with the xstr() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xt function ] (XTuple)
|
||||||
|
# Cast a Python list, set, tuple to a XTuple
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xt(tup_arg, attributes={}):
|
||||||
|
try:
|
||||||
|
return XTuple(tup_arg, attributes)
|
||||||
|
except TypeError as te:
|
||||||
|
raise TypeError("Attempted to cast to a XTuple with an incompatible type")
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
print("Naked Framework Error: unable to cast object to a XTuple with the xt() function (Naked.toolshed.casts.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,376 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ IO class ]
|
||||||
|
# interface for all local file IO classes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class IO:
|
||||||
|
def __init__(self,filepath):
|
||||||
|
self.filepath = filepath
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ FileWriter class ]
|
||||||
|
# writes data to local files
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class FileWriter(IO):
|
||||||
|
def __init__(self, filepath):
|
||||||
|
IO.__init__(self, filepath)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ append method ]
|
||||||
|
# Universal text file writer that appends to existing file using system default text encoding or utf-8 if throws unicode error
|
||||||
|
# Tests: test_IO.py:: test_file_ascii_readwrite_append, test_file_append_missingfile
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def append(self, text):
|
||||||
|
try:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if not file_exists(self.filepath): #confirm that file exists, if not raise IOError (assuming that developer expected existing file if using append)
|
||||||
|
raise IOError("The file specified for the text append does not exist (Naked.toolshed.file.py:append).")
|
||||||
|
with open(self.filepath, 'a') as appender:
|
||||||
|
appender.write(text)
|
||||||
|
except UnicodeEncodeError as ue:
|
||||||
|
self.append_utf8(text) #try writing as utf-8
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to append text to the file with the append() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ append_utf8 method ]
|
||||||
|
# Text writer that appends text to existing file with utf-8 encoding
|
||||||
|
# Tests: test_IO.py :: test_file_utf8_readwrite_append
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def append_utf8(self, text):
|
||||||
|
try:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if not file_exists(self.filepath):
|
||||||
|
raise IOError("The file specified for the text append does not exist (Naked.toolshed.file.py:append_utf8).")
|
||||||
|
import codecs
|
||||||
|
import unicodedata
|
||||||
|
norm_text = unicodedata.normalize('NFKD', text) # NKFD normalization of the unicode data before write
|
||||||
|
with codecs.open(self.filepath, mode='a', encoding="utf_8") as appender:
|
||||||
|
appender.write(norm_text)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to append text to the file with the append_utf8 method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ gzip method (writer) ]
|
||||||
|
# writes data to gzip compressed file
|
||||||
|
# Note: adds .gz extension to filename if user did not specify it in the FileWriter class constructor
|
||||||
|
# Note: uses compresslevel = 6 as default to balance speed and compression level (which in general is not significantly less than 9)
|
||||||
|
# Tests: test_IO.py :: test_file_gzip_ascii_readwrite, test_file_gzip_utf8_readwrite,
|
||||||
|
# test_file_gzip_utf8_readwrite_explicit_decode
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def gzip(self, text, compression_level=6):
|
||||||
|
try:
|
||||||
|
import gzip
|
||||||
|
if not self.filepath.endswith(".gz"):
|
||||||
|
self.filepath = self.filepath + ".gz"
|
||||||
|
with gzip.open(self.filepath, 'wb', compresslevel=compression_level) as gzip_writer:
|
||||||
|
gzip_writer.write(text)
|
||||||
|
except UnicodeEncodeError as ue:
|
||||||
|
import unicodedata
|
||||||
|
norm_text = unicodedata.normalize('NFKD', text) # NKFD normalization of the unicode data before write
|
||||||
|
import codecs
|
||||||
|
binary_data = codecs.encode(norm_text, "utf_8")
|
||||||
|
with gzip.open(self.filepath, 'wb', compresslevel=compression_level) as gzip_writer:
|
||||||
|
gzip_writer.write(binary_data)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to gzip compress the file with the gzip method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ write method ]
|
||||||
|
# Universal text file writer that writes by system default or utf-8 encoded unicode if throws UnicdeEncodeError
|
||||||
|
# Tests: test_IO.py :: test_file_ascii_readwrite, test_file_ascii_readwrite_missing_file,
|
||||||
|
# test_file_utf8_write_raises_unicodeerror
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def write(self, text):
|
||||||
|
try:
|
||||||
|
with open(self.filepath, 'wt') as writer:
|
||||||
|
writer.write(text)
|
||||||
|
except UnicodeEncodeError as ue:
|
||||||
|
self.write_utf8(text) # attempt to write with utf-8 encoding
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to write to requested file with the write() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ write_as method ]
|
||||||
|
# text file writer that uses developer specified text encoding
|
||||||
|
# Tests: test_IO.py :: test_file_utf8_readas_writeas
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def write_as(self, text, the_encoding=""):
|
||||||
|
try:
|
||||||
|
if the_encoding == "": #if the developer did not include the encoding type, raise an exception
|
||||||
|
raise RuntimeError("The text encoding was not specified as an argument to the write_as() method (Naked.toolshed.file.py:write_as).")
|
||||||
|
import codecs
|
||||||
|
with codecs.open(self.filepath, encoding=the_encoding, mode='w') as f:
|
||||||
|
f.write(text)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to write file with the specified encoding using the write_as() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ write_bin method ]
|
||||||
|
# binary data file writer
|
||||||
|
# Tests: test_IO.py :: test_file_bin_readwrite
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def write_bin(self, binary_data):
|
||||||
|
try:
|
||||||
|
with open(self.filepath, 'wb') as bin_writer:
|
||||||
|
bin_writer.write(binary_data)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to write binary data to file with the write_bin method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ safe_write method ] (boolean)
|
||||||
|
# Universal text file writer (writes in default encoding unless throws unicode error) that will NOT overwrite existing file at the requested filepath
|
||||||
|
# returns boolean indicator for success of write based upon test for existence of file (False = write failed because file exists)
|
||||||
|
# Tests: test_IO.py :: test_file_ascii_safewrite, test_file_utf8_safewrite
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def safe_write(self, text):
|
||||||
|
import os.path
|
||||||
|
if not os.path.exists(self.filepath): # if the file does not exist, then can write
|
||||||
|
try:
|
||||||
|
with open(self.filepath, 'wt') as writer:
|
||||||
|
writer.write(text)
|
||||||
|
return True
|
||||||
|
except UnicodeEncodeError as ue:
|
||||||
|
self.write_utf8(text)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to write to requested file with the safe_write() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
else:
|
||||||
|
return False # if file exists, do not write and return False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ safe_write_bin method ]
|
||||||
|
# Binary data file writer that will NOT overwrite existing file at the requested filepath
|
||||||
|
# returns boolean indicator for success of write based upon test for existence of file (False = write failed because file exists)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def safe_write_bin(self, file_data):
|
||||||
|
try:
|
||||||
|
import os.path
|
||||||
|
if not os.path.exists(self.filepath):
|
||||||
|
with open(self.filepath, 'wb') as writer:
|
||||||
|
writer.write(file_data)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to write to requested file with the safe_write_bin() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ write_utf8 method ]
|
||||||
|
# Text file writer with explicit UTF-8 text encoding
|
||||||
|
# uses filepath from class constructor
|
||||||
|
# requires text to passed as a method parameter
|
||||||
|
# Tests: test_IO.py :: test_file_utf8_readwrite, test_file_utf8_readwrite_raises_unicodeerror
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def write_utf8(self, text):
|
||||||
|
try:
|
||||||
|
import codecs
|
||||||
|
f = codecs.open(self.filepath, encoding='utf_8', mode='w')
|
||||||
|
except IOError as ioe:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to open file for write with the write_utf8() method (Naked.toolshed.file.py).")
|
||||||
|
raise ioe
|
||||||
|
try:
|
||||||
|
import unicodedata
|
||||||
|
norm_text = unicodedata.normalize('NFKD', text) # NKFD normalization of the unicode data before write
|
||||||
|
f.write(norm_text)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to write UTF-8 encoded text to file with the write_utf8() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ FileReader class ]
|
||||||
|
# reads data from local files
|
||||||
|
# filename assigned in constructor (inherited from IO class interface)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class FileReader(IO):
|
||||||
|
def __init__(self, filepath):
|
||||||
|
IO.__init__(self, filepath)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ read method ] (string)
|
||||||
|
# Universal text file reader that will read utf-8 encoded unicode or non-unicode text as utf-8
|
||||||
|
# returns string or unicode (py3 = string for unicode and non-unicode, py2 = str for non-unicode, unicode for unicode)
|
||||||
|
# Tests: test_IO.py :: test_file_ascii_readwrite, test_file_read_missing_file,
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def read(self):
|
||||||
|
try:
|
||||||
|
return self.read_utf8() #reads everything as unicode in utf8 encoding
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read text from the requested file with the read() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ read_bin method ] (binary byte string)
|
||||||
|
# Universal binary data file reader
|
||||||
|
# returns file contents in binary mode as binary byte strings
|
||||||
|
# Tests: test_IO.py :: test_file_bin_readwrite, test_file_read_bin_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def read_bin(self):
|
||||||
|
try:
|
||||||
|
with open(self.filepath, 'rb') as bin_reader:
|
||||||
|
data = bin_reader.read()
|
||||||
|
return data
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read the binary data from the file with the read_bin method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ read_as method ] (string with developer specified text encoding)
|
||||||
|
# Text file reader with developer specified text encoding
|
||||||
|
# returns file contents in developer specified text encoding
|
||||||
|
# Tests: test_IO.py :: test_file_utf8_readas_writeas, test_file_readas_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def read_as(self, the_encoding):
|
||||||
|
try:
|
||||||
|
if the_encoding == "":
|
||||||
|
raise RuntimeError("The text file encoding was not specified as an argument to the read_as method (Naked.toolshed.file.py:read_as).")
|
||||||
|
import codecs
|
||||||
|
with codecs.open(self.filepath, encoding=the_encoding, mode='r') as f:
|
||||||
|
data = f.read()
|
||||||
|
return data
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read the file with the developer specified text encoding with the read_as method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ readlines method ] (list of strings)
|
||||||
|
# Read text from file line by line, uses utf8 encoding by default
|
||||||
|
# returns list of utf8 encoded file lines as strings
|
||||||
|
# Tests: test_IO.py :: test_file_readlines, test_file_readlines_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def readlines(self):
|
||||||
|
try:
|
||||||
|
return self.readlines_utf8() # read as utf8 encoded file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read text from the requested file with the readlines() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ readlines_as method ] (list of developer specified encoded strings)
|
||||||
|
# Read lines from file with developer specified text encoding
|
||||||
|
# Returns a list of developer specified encoded lines from the file
|
||||||
|
# Tests: test_IO.py ::
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def readlines_as(self, dev_spec_encoding):
|
||||||
|
try:
|
||||||
|
if dev_spec_encoding == "":
|
||||||
|
raise RuntimeError("The text file encoding was not specified as an argument to the readlines_as method (Naked.toolshed.file.py:readlines_as).")
|
||||||
|
import codecs
|
||||||
|
with codecs.open(self.filepath, encoding=dev_spec_encoding, mode='r') as reader:
|
||||||
|
data_list = []
|
||||||
|
for line in reader:
|
||||||
|
data_list.append(line)
|
||||||
|
return data_list
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to read lines in the specified encoding with the readlines_as method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ readlines_utf8 method ] (list of utf-8 encoded strings)
|
||||||
|
# Read text from unicode file by line
|
||||||
|
# Returns list of file unicode text lines as unicode strings
|
||||||
|
# Tests: test_IO.py :: test_file_readlines_unicode, test_file_readlines_utf8_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def readlines_utf8(self):
|
||||||
|
try:
|
||||||
|
import codecs
|
||||||
|
with codecs.open(self.filepath, encoding='utf-8', mode='r') as uni_reader:
|
||||||
|
modified_text_list = []
|
||||||
|
for line in uni_reader:
|
||||||
|
import unicodedata
|
||||||
|
norm_line = unicodedata.normalize('NFKD', line) # NKFD normalization of the unicode data before use
|
||||||
|
modified_text_list.append(norm_line)
|
||||||
|
return modified_text_list
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to read lines in the unicode file with the readlines_utf8 method (Naked.toolshed.file.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ read_gzip ] (byte string)
|
||||||
|
# reads data from a gzip compressed file
|
||||||
|
# returns the decompressed binary data from the file
|
||||||
|
# Note: if decompressing unicode file, set encoding="utf-8"
|
||||||
|
# Tests: test_IO.py :: test_file_gzip_ascii_readwrite, test_file_gzip_utf8_readwrite,
|
||||||
|
# test_file_read_gzip_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def read_gzip(self, encoding="system_default"):
|
||||||
|
try:
|
||||||
|
import gzip
|
||||||
|
with gzip.open(self.filepath, 'rb') as gzip_reader:
|
||||||
|
file_data = gzip_reader.read()
|
||||||
|
if encoding in ["utf-8", "utf8", "utf_8", "UTF-8", "UTF8", "UTF_8"]:
|
||||||
|
import codecs
|
||||||
|
file_data = codecs.decode(file_data, "utf-8")
|
||||||
|
import unicodedata
|
||||||
|
norm_data = unicodedata.normalize('NFKD', file_data) # NKFD normalization of the unicode data before passing back to the caller
|
||||||
|
return norm_data
|
||||||
|
else:
|
||||||
|
return file_data
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read from the gzip compressed file with the read_gzip() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ read_utf8 method ] (string)
|
||||||
|
# read data from a file with explicit UTF-8 encoding
|
||||||
|
# uses filepath from class constructor
|
||||||
|
# returns a unicode string containing the file data (unicode in py2, str in py3)
|
||||||
|
# Tests: test_IO.py :: test_file_utf8_readwrite, test_file_utf8_readwrite_append,
|
||||||
|
# test_file_read_utf8_missing_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def read_utf8(self):
|
||||||
|
try:
|
||||||
|
import codecs
|
||||||
|
f = codecs.open(self.filepath, encoding='utf_8', mode='r')
|
||||||
|
except IOError as ioe:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to open file for read with read_utf8() method (Naked.toolshed.file.py).")
|
||||||
|
raise ioe
|
||||||
|
try:
|
||||||
|
textstring = f.read()
|
||||||
|
import unicodedata
|
||||||
|
norm_text = unicodedata.normalize('NFKD', textstring) # NKFD normalization of the unicode data before returns
|
||||||
|
return norm_text
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to read the file with UTF-8 encoding using the read_utf8() method (Naked.toolshed.file.py).")
|
||||||
|
raise e
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# The Ink Templating System
|
||||||
|
# A lightweight, fast, flexible text templating system
|
||||||
|
# Copyright 2014 Christopher Simpkins
|
||||||
|
# MIT License
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
import re
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Template class
|
||||||
|
# A template string class that is inherited from Python str
|
||||||
|
# Includes metadata about the template:
|
||||||
|
# odel = opening delimiter
|
||||||
|
# cdel = closing delimiter
|
||||||
|
# varlist = inclusive list of all variables in the template text (parsed in constructor)
|
||||||
|
# Delimiters:
|
||||||
|
# default = {{variable}}
|
||||||
|
# assign new opening and closing delimiters as parameters when you make a new Template instance
|
||||||
|
# `escape_regex` boolean is a speedup, avoids Python escape of special regex chars if you do not need it
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class Template(str):
|
||||||
|
def __new__(cls, template_text, open_delimiter="{{", close_delimiter="}}", escape_regex=False):
|
||||||
|
obj = str.__new__(cls, template_text)
|
||||||
|
obj.odel = open_delimiter
|
||||||
|
obj.cdel = close_delimiter
|
||||||
|
obj.varlist = obj._make_var_list(template_text, escape_regex) #contains all unique parsed variables from the template in a list
|
||||||
|
return obj
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _make_var_list method ] (list of strings)
|
||||||
|
# Private method that parses the template string for all variables that match the delimiter pattern
|
||||||
|
# Returns a list of the variable names as strings
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _make_var_list(self, template_text, escape_regex=False):
|
||||||
|
if escape_regex:
|
||||||
|
open_match_pat = self._escape_regex_special_chars(self.odel)
|
||||||
|
close_match_pat = self._escape_regex_special_chars(self.cdel)
|
||||||
|
match_pat = open_match_pat + r'(.*?)' + close_match_pat # capture group contains the variable name used between the opening and closing delimiters
|
||||||
|
else:
|
||||||
|
match_pat = self.odel + r'(.*?)' + self.cdel
|
||||||
|
var_list = re.findall(match_pat, template_text) #generate a list that contains the capture group from the matches (i.e. the variables in the template)
|
||||||
|
return set(var_list) # remove duplicate entries by converting to set (and lookup speed improvement from hashing)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _escape_regex_special_chars method ] (string)
|
||||||
|
# Private method that escapes special regex metacharacters
|
||||||
|
# Returns a string with the escaped character modifications
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _escape_regex_special_chars(self, test_escape_string):
|
||||||
|
return re.escape(test_escape_string)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Renderer class
|
||||||
|
# Render the variable replacements in the ink template using a Python dictionary key argument
|
||||||
|
# Construct the instace of the Renderer with the Ink template and the dictionary key
|
||||||
|
# Run the renderer with the render method on the instance (e.g. r.render())
|
||||||
|
# Parameters to constructor:
|
||||||
|
# - template = an Ink Template instance
|
||||||
|
# - key = a dictionary mapped key = variable name : value = variable replacement data
|
||||||
|
# - html_entities = encode html entities with HTML escaped characters (default = False = do not encode)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class Renderer:
|
||||||
|
def __init__(self, template, key, html_entities=False):
|
||||||
|
self.odel = template.odel
|
||||||
|
self.cdel = template.cdel
|
||||||
|
self.template = template
|
||||||
|
self.html_entities = html_entities
|
||||||
|
self.key_dict = key
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ render method ] (string)
|
||||||
|
# renders the variable replacements in the Ink template
|
||||||
|
# returns the rendered template as a string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def render(self):
|
||||||
|
# make local variables for the loop below (faster)
|
||||||
|
local_dict = self.key_dict
|
||||||
|
local_template = self.template
|
||||||
|
local_varlist = self.template.varlist
|
||||||
|
local_odel = self.odel
|
||||||
|
local_cdel = self.cdel
|
||||||
|
local_htmlent = self.html_entities
|
||||||
|
if local_htmlent:
|
||||||
|
from xml.sax.saxutils import escape #from Python std lib
|
||||||
|
for key in local_dict:
|
||||||
|
if key in local_varlist:
|
||||||
|
value = local_dict[key]
|
||||||
|
replace_string = local_odel + key + local_cdel
|
||||||
|
if local_htmlent:
|
||||||
|
value = escape(value) #xml.sax.saxutils function
|
||||||
|
local_template = local_template.replace(replace_string, value)
|
||||||
|
return local_template
|
||||||
|
|
||||||
|
##TODO : multiple file render method?
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
||||||
|
# template = Template("This is a of the {{test}} of the {{document}} {{type}} and more of the {{test}} {{document}} {{type}}")
|
||||||
|
# renderer = Renderer(template, {'test': 'ব য', 'document':'testing document', 'type':'of mine', 'bogus': 'bogus test'})
|
||||||
|
# print(renderer.render())
|
@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ chain_iter method ] (iterable items of type contained in multiple list arguments)
|
||||||
|
# Generator that returns iterable for each item in the multiple list arguments in sequence
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def chain_iter(self, *lists):
|
||||||
|
return itertools.chain(*lists)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,351 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import requests
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#[ HTTP class]
|
||||||
|
# handle HTTP requests
|
||||||
|
# Uses the requests external library to handle HTTP requests and response object (available on PyPI)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class HTTP():
|
||||||
|
def __init__(self, url="", request_timeout=10):
|
||||||
|
self.url = url
|
||||||
|
self.request_timeout = request_timeout
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# HTTP response properties (assignment occurs with the HTTP request methods)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
self.res = None # assigned with the requests external library response object after a HTTP method call
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get method ] (string) -
|
||||||
|
# HTTP GET request - returns text string
|
||||||
|
# returns data stream read from the URL (string)
|
||||||
|
# Default timeout = 10 s from class constructor
|
||||||
|
# Re-throws ConnectionError on failed connection (e.g. no site at URL)
|
||||||
|
# Test : test_NETWORK.py :: test_http_get, test_http_get_response_change,
|
||||||
|
# test_http_post_reponse_change, test_http_get_response_check
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get(self, follow_redirects=True):
|
||||||
|
try:
|
||||||
|
response = requests.get(self.url, timeout=self.request_timeout, allow_redirects=follow_redirects)
|
||||||
|
self.res = response # assign the response object from requests to a property on the instance of HTTP class
|
||||||
|
return response.text
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform GET request with the URL " + self.url + " using the get() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_data method ] (binary data)
|
||||||
|
# HTTP GET request, return binary data
|
||||||
|
# returns data stream with raw binary data
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_bin(self):
|
||||||
|
try:
|
||||||
|
response = requests.get(self.url, timeout=self.request_timeout)
|
||||||
|
self.res = response # assign the response object from requests to a property on the instance
|
||||||
|
return response.content # return binary data instead of text (get() returns text)
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform GET request with the URL " + self.url + " using the get_data() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_bin_write_file method ] (boolean)
|
||||||
|
# open HTTP data stream with GET request, make file with the returned binary data
|
||||||
|
# file path is passed to the method by the developer
|
||||||
|
# set suppress_output to True if you want to suppress the d/l status information that is printed to the standard output stream
|
||||||
|
# return True on successful pull and write to disk
|
||||||
|
# Tests: test_NETWORK.py :: test_http_get_binary
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_bin_write_file(self, filepath="", suppress_output = False, overwrite_existing = False):
|
||||||
|
try:
|
||||||
|
import os # used for os.fsync() method in the write
|
||||||
|
# Confirm that the file does not exist and prevent overwrite if it does (unless developer indicates otherwise)
|
||||||
|
if not overwrite_existing:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if file_exists(filepath):
|
||||||
|
if not suppress_output:
|
||||||
|
print("Download aborted. A local file with the requested filename exists on the path.")
|
||||||
|
return False
|
||||||
|
if (filepath == "" and len(self.url) > 1):
|
||||||
|
filepath = self.url.split('/')[-1] # use the filename from URL and working directory as default if not specified
|
||||||
|
if not suppress_output:
|
||||||
|
sys.stdout.write("Downloading file from " + self.url + "...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
response = requests.get(self.url, timeout=self.request_timeout, stream=True)
|
||||||
|
self.res = response
|
||||||
|
with open(filepath, 'wb') as f: # write as binary data
|
||||||
|
for chunk in response.iter_content(chunk_size=2048):
|
||||||
|
f.write(chunk)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno()) # flush all internal buffers to disk
|
||||||
|
if not suppress_output:
|
||||||
|
print(" ")
|
||||||
|
print("Download complete.")
|
||||||
|
return True # return True if successful write
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform GET request and write file with the URL " + self.url + " using the get_bin_write_file() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_txt_write_file method ] (boolean)
|
||||||
|
# open HTTP data stream with GET request, write file with utf-8 encoded text using returned text data
|
||||||
|
# file path is passed to the method by the developer (default is the base filename in the URL if not specified)
|
||||||
|
# return True on successful pull and write to disk
|
||||||
|
# Tests: test_NETWORK.py :: test_http_get_text
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_txt_write_file(self, filepath="", suppress_output = False, overwrite_existing = False):
|
||||||
|
try:
|
||||||
|
import os # used for os.fsync() method in the write
|
||||||
|
# Confirm that the file does not exist and prevent overwrite if it does (unless developer indicates otherwise)
|
||||||
|
if not overwrite_existing:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if file_exists(filepath):
|
||||||
|
if not suppress_output:
|
||||||
|
print("Download aborted. A local file with the requested filename exists on the path.")
|
||||||
|
return False
|
||||||
|
if (filepath == "" and len(self.url) > 1):
|
||||||
|
filepath = self.url.split('/')[-1] # use the filename from URL and working directory as default if not specified
|
||||||
|
if not suppress_output:
|
||||||
|
sys.stdout.write("Downloading file from " + self.url + "...")
|
||||||
|
sys.stdout.flush()
|
||||||
|
response = requests.get(self.url, timeout=self.request_timeout, stream=True)
|
||||||
|
self.res = response
|
||||||
|
import codecs
|
||||||
|
with codecs.open(filepath, mode='w', encoding="utf-8") as f: #write as text
|
||||||
|
for chunk in response.iter_content(chunk_size=2048):
|
||||||
|
chunk = chunk.decode('utf-8')
|
||||||
|
f.write(chunk)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno()) # flush all internal buffers to disk
|
||||||
|
if not suppress_output:
|
||||||
|
print(" ")
|
||||||
|
print("Download complete.")
|
||||||
|
return True # return True if successful write
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform GET request and write file with the URL " + self.url + " using the get_data_write_txt() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ head method ] (dictionary of strings)
|
||||||
|
# HTTP HEAD request
|
||||||
|
# returns a dictionary of the header strings
|
||||||
|
# test for a specific header on either the response dictionary or the instance res property
|
||||||
|
# Usage example:
|
||||||
|
# content_type = instance.res['content-type']
|
||||||
|
# Tests: test_NETWORK.py :: test_http_head
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def head(self):
|
||||||
|
try:
|
||||||
|
response = requests.head(self.url, timeout=self.request_timeout)
|
||||||
|
self.res = response # assign the response object from requests to a property on the instance of HTTP class
|
||||||
|
return response.headers
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform a HEAD request with the head() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ post method ] (string)
|
||||||
|
# HTTP POST request for text
|
||||||
|
# returns text from the URL as a string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def post(self, follow_redirects=True):
|
||||||
|
try:
|
||||||
|
response = requests.post(self.url, timeout=self.request_timeout, allow_redirects=follow_redirects)
|
||||||
|
self.res = response # assign the response object from requests to a property on the instance of HTTP class
|
||||||
|
return response.text
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.exit("Naked Framework Error: Unable to perform a POST request with the post() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ post_bin method ] (binary data)
|
||||||
|
# HTTP POST request for binary data
|
||||||
|
# returns binary data from the URL
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def post_bin(self):
|
||||||
|
try:
|
||||||
|
response = requests.post(self.url, timeout=self.request_timeout)
|
||||||
|
self.res = response # assign the response object from requests to a property on the instance of HTTP class
|
||||||
|
return response.content
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.exit("Naked Framework Error: Unable to perform POST request with the post_bin() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ post_bin_write_file method ] (boolean = success of write)
|
||||||
|
# HTTP POST request, write binary file with the response data
|
||||||
|
# default filepath is the basename of the URL file, may be set by passing an argument to the method
|
||||||
|
# returns a boolean that indicates the success of the file write
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def post_bin_write_file(self, filepath="", suppress_output = False, overwrite_existing = False):
|
||||||
|
try:
|
||||||
|
import os # used for os.fsync() method in the write
|
||||||
|
# Confirm that the file does not exist and prevent overwrite if it does (unless developer indicates otherwise)
|
||||||
|
if not overwrite_existing:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if file_exists(filepath):
|
||||||
|
if not suppress_output:
|
||||||
|
print("Download aborted. A local file with the requested filename exists on the path.")
|
||||||
|
return False
|
||||||
|
if (filepath == "" and len(self.url) > 1):
|
||||||
|
filepath = self.url.split('/')[-1] # use the filename from URL and working directory as default if not specified
|
||||||
|
if not suppress_output:
|
||||||
|
sys.stdout.write("Downloading file from " + self.url + "...") #provide information about the download to user
|
||||||
|
sys.stdout.flush()
|
||||||
|
response = requests.post(self.url, timeout=self.request_timeout, stream=True)
|
||||||
|
self.res = response
|
||||||
|
with open(filepath, 'wb') as f: # write as binary data
|
||||||
|
for chunk in response.iter_content(chunk_size=2048):
|
||||||
|
f.write(chunk)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno()) # flush all internal buffers to disk
|
||||||
|
if not suppress_output:
|
||||||
|
print(" ")
|
||||||
|
print("Download complete.") # provide user with completion information
|
||||||
|
return True # return True if successful write
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform POST request and write file with the URL " + self.url + " using the post_data_write_bin() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ post_txt_write_file method ] (boolean = success of file write)
|
||||||
|
# HTTP POST request, write utf-8 encoded text file with the response data
|
||||||
|
# default filepath is the basename of the URL file, may be set by passing an argument to the method
|
||||||
|
# returns a boolean that indicates the success of the file write
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def post_txt_write_file(self, filepath="", suppress_output = False, overwrite_existing = False):
|
||||||
|
try:
|
||||||
|
import os # used for os.fsync() method in the write
|
||||||
|
# Confirm that the file does not exist and prevent overwrite if it does (unless developer indicates otherwise)
|
||||||
|
if not overwrite_existing:
|
||||||
|
from Naked.toolshed.system import file_exists
|
||||||
|
if file_exists(filepath):
|
||||||
|
if not suppress_output:
|
||||||
|
print("Download aborted. A local file with the requested filename exists on the path.")
|
||||||
|
return False
|
||||||
|
if (filepath == "" and len(self.url) > 1):
|
||||||
|
filepath = self.url.split('/')[-1] # use the filename from URL and working directory as default if not specified
|
||||||
|
if not suppress_output:
|
||||||
|
sys.stdout.write("Downloading file from " + self.url + "...") #provide information about the download to user
|
||||||
|
sys.stdout.flush()
|
||||||
|
response = requests.post(self.url, timeout=self.request_timeout, stream=True)
|
||||||
|
self.res = response
|
||||||
|
import codecs
|
||||||
|
with codecs.open(filepath, mode='w', encoding="utf-8") as f: # write as binary data
|
||||||
|
for chunk in response.iter_content(chunk_size=2048):
|
||||||
|
chunk = chunk.decode('utf-8')
|
||||||
|
f.write(chunk)
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno()) # flush all internal buffers to disk
|
||||||
|
if not suppress_output:
|
||||||
|
print(" ")
|
||||||
|
print("Download complete.") # provide user with completion information
|
||||||
|
return True # return True if successful write
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to perform POST request and write file with the URL " + self.url + " using the post_data_write_bin() method (Naked.toolshed.network.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ response method ]
|
||||||
|
# getter method for the requests library object that is assigned as a property
|
||||||
|
# on the HTTP class after a HTTP request method is run (e.g. get())
|
||||||
|
# Note: must run one of the HTTP request verbs to assign this property before use of getter (=None by default)
|
||||||
|
# Tests: test_NETWORK.py :: test_http_get_response_check
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def response(self):
|
||||||
|
try:
|
||||||
|
return self.res
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to return the response from your HTTP request with the response() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_status_ok method ] (boolean)
|
||||||
|
# return boolean whether HTTP response was in 200 status code range for GET request
|
||||||
|
# Note: this method runs its own GET request, does not need to be run separately
|
||||||
|
# Tests: test_NETWORK.py ::
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_status_ok(self):
|
||||||
|
try:
|
||||||
|
self.get() #run the get request
|
||||||
|
if self.res and self.res.status_code:
|
||||||
|
return (self.res.status_code == requests.codes.ok)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to obtain the HTTP status with the get_status_ok() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ post_status_ok method ] (boolean)
|
||||||
|
# return boolean whether HTTP response was in 200 status code range for POST request
|
||||||
|
# Note: method runs its own post method, not necessary to run separately
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def post_status_ok(self):
|
||||||
|
try:
|
||||||
|
self.post() #run the post request
|
||||||
|
if self.res and self.res.status_code:
|
||||||
|
return (self.res.status_code == requests.codes.ok)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except requests.exceptions.ConnectionError as ce:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Unable to obtain the HTTP status with the post_status_ok() method (Naked.toolshed.network.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# HTTP GET 1
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# http = HTTP("http://www.google.com")
|
||||||
|
# data = http.get()
|
||||||
|
# print(data)
|
||||||
|
# from Naked.toolshed.file import FileWriter
|
||||||
|
# w = FileWriter("testfile.txt")
|
||||||
|
# w.write_utf8(data)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# HTTP GET 2
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# http = HTTP()
|
||||||
|
# http.url = "http://www.google.com"
|
||||||
|
# print(http.get())
|
||||||
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# tests: test_PYTHON.py
|
||||||
|
|
||||||
|
from sys import version_info
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Python Versions
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ py_version function ] (tuple of (major, minor, patch))
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def py_version():
|
||||||
|
return (version_info[0], version_info[1], version_info[2])
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ py_major_version function ] (integer)
|
||||||
|
# Return Python interpreter major version number
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def py_major_version():
|
||||||
|
return (version_info[0])
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ py_minor_version function ] (integer)
|
||||||
|
# Return Python interpreter minor version number
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def py_minor_version():
|
||||||
|
return (version_info[1])
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ py_patch_version function ] (integer)
|
||||||
|
# Return Python interpreter patch version number
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def py_patch_version():
|
||||||
|
return (version_info[2])
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_py2 function ] (boolean)
|
||||||
|
# Return truth result for question is interpreter running a version of Python 2
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_py2():
|
||||||
|
return (version_info[0] == (2))
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_py3 function ] (boolean)
|
||||||
|
# Return truth result for question is interpreter running a version of Python 3
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_py3():
|
||||||
|
return (version_info[0] == (3))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,263 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ execute function ] (boolean)
|
||||||
|
# run a shell command and print std out / std err to terminal
|
||||||
|
# returns True if exit status = 0
|
||||||
|
# returns False if exit status != 0
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def execute(command):
|
||||||
|
try:
|
||||||
|
response = subprocess.call(command, shell=True)
|
||||||
|
if response == 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except subprocess.CalledProcessError as cpe:
|
||||||
|
try:
|
||||||
|
sys.stderr.write(cpe.output)
|
||||||
|
except TypeError as te:
|
||||||
|
sys.stderr.write(str(cpe.output))
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the execute() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ run function ] (bytes string or False)
|
||||||
|
# run a shell command
|
||||||
|
# default =
|
||||||
|
# success:: print to std out and return the std out string
|
||||||
|
# error:: print to stderr return False, suppress SystemExit on error to permit ongoing run of calling script
|
||||||
|
# suppress_stdout = True >> suppress std output stream print (returns string)
|
||||||
|
# suppress_stderr = True >> suppress std err stream print (returns False)
|
||||||
|
# suppress_exit_status_call = False >> raise SystemExit with the returned status code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def run(command, suppress_stdout=False, suppress_stderr=False, suppress_exit_status_call=True):
|
||||||
|
try:
|
||||||
|
response = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
|
||||||
|
if not suppress_stdout:
|
||||||
|
print(response)
|
||||||
|
return response
|
||||||
|
except subprocess.CalledProcessError as cpe:
|
||||||
|
if not suppress_stderr: # error in existing application (non-zero exit status)
|
||||||
|
try:
|
||||||
|
sys.stderr.write(cpe.output)
|
||||||
|
except TypeError as te: # deal with unusual errors from some system executables that return non string type through subprocess.check_output
|
||||||
|
sys.stderr.write(str(cpe.output))
|
||||||
|
if not suppress_exit_status_call:
|
||||||
|
if cpe.returncode:
|
||||||
|
sys.exit(cpe.returncode)
|
||||||
|
else:
|
||||||
|
sys.exit(1)
|
||||||
|
return False # return False on non-zero exit status codes (i.e. failures in the subprocess executable)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the run() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ muterun function ] (NakedObject with attributes for stdout, stderr, exitcode)
|
||||||
|
# run a shell command and return a response object
|
||||||
|
# return object attributes : stdout (bytes), stderr (bytes), exitcode (int)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def muterun(command):
|
||||||
|
try:
|
||||||
|
from Naked.toolshed.types import NakedObject
|
||||||
|
response_obj = NakedObject()
|
||||||
|
response = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
|
||||||
|
response_obj.stdout = response
|
||||||
|
response_obj.exitcode = 0
|
||||||
|
response_obj.stderr = b""
|
||||||
|
return response_obj
|
||||||
|
except subprocess.CalledProcessError as cpe:
|
||||||
|
response_obj.stdout = b""
|
||||||
|
response_obj.stderr = cpe.output
|
||||||
|
if cpe.returncode:
|
||||||
|
response_obj.exitcode = cpe.returncode
|
||||||
|
else:
|
||||||
|
response_obj.exitcode = 1
|
||||||
|
return response_obj
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the mute_run() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# RUBY COMMAND EXECUTION
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ execute_rb function ] (boolean)
|
||||||
|
# execute a ruby script file in a shell subprocess
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def execute_rb(file_path, arguments=""):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
rb_command = 'ruby ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
rb_command = 'ruby ' + file_path
|
||||||
|
return execute(rb_command) # return result of execute() of the ruby file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the run_rb() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ run_rb function ] (bytes string or False)
|
||||||
|
# execute a ruby script file in a shell subprocess, return the output
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def run_rb(file_path, arguments="", suppress_stdout=False, suppress_stderr=False, suppress_exit_status_call=True):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
rb_command = 'ruby ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
rb_command = 'ruby ' + file_path
|
||||||
|
return run(rb_command, suppress_stdout, suppress_stderr, suppress_exit_status_call) # return result of run() of the ruby file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the run_rb() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ muterun_rb function ] (NakedObject response object)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def muterun_rb(file_path, arguments=""):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
rb_command = 'ruby ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
rb_command = 'ruby ' + file_path
|
||||||
|
return muterun(rb_command) # return result of muterun() of the ruby file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the muterun_rb() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# NODE.JS COMMAND EXECUTION
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ execute_js function ] (boolean)
|
||||||
|
# execute a node.js script file in a shell subprocess
|
||||||
|
# stdout stream to terminal
|
||||||
|
# returns True for success (=0) exit status code
|
||||||
|
# returns False for unsuccessful (!=0) exit status code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def execute_js(file_path, arguments=""):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
js_command = 'node ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
js_command = 'node ' + file_path
|
||||||
|
return execute(js_command) # return result of execute() of node.js file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the run_js() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ run_js function ] (byte string or False)
|
||||||
|
# execute a node.js script file in a shell subprocess
|
||||||
|
# print the standard output to the standard output stream by default
|
||||||
|
# set suppress_output to True to suppress stream to standard output. String is still returned to calling function
|
||||||
|
# set suppress_exit_status_call to True to suppress raising sys.exit on failures with shell subprocess exit status code (if available) or 1 if not available
|
||||||
|
# returns the standard output byte string from the subprocess executable on success
|
||||||
|
# returns False if the subprocess exits with a non-zero exit code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def run_js(file_path, arguments="", suppress_stdout=False, suppress_stderr=False, suppress_exit_status_call=True):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
js_command = 'node ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
js_command = 'node ' + file_path
|
||||||
|
return run(js_command, suppress_stdout, suppress_stderr, suppress_exit_status_call) # return result of run() of node.js file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the run_js() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ muterun_js function ] (NakedObject response object)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def muterun_js(file_path, arguments=""):
|
||||||
|
try:
|
||||||
|
if len(arguments) > 0:
|
||||||
|
js_command = 'node ' + file_path + " " + arguments
|
||||||
|
else:
|
||||||
|
js_command = 'node ' + file_path
|
||||||
|
return muterun(js_command) # return result of muterun() of node.js file
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to run the shell command with the muterun_js() function (Naked.toolshed.shell.py).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ Environment Class ]
|
||||||
|
# shell environment variables class
|
||||||
|
# self.env = the environment variable dictionary
|
||||||
|
# self.vars = the environment variable names list
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class Environment():
|
||||||
|
def __init__(self):
|
||||||
|
self.env = os.environ
|
||||||
|
self.vars = list(os.environ.keys())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_var method ] (boolean)
|
||||||
|
# return boolean for presence of a variable name in the shell environment
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_var(self, var_name):
|
||||||
|
try:
|
||||||
|
return (var_name in self.vars)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to determine if the variable is included in the shell variable list with the is_var() method (Naked.toolshed.shell).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_var method ] (string)
|
||||||
|
# get the variable value for a variable in the shell environment list
|
||||||
|
# returns empty string if the variable is not included in the environment variable list
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_var(self, var_name):
|
||||||
|
try:
|
||||||
|
if var_name in self.vars:
|
||||||
|
return self.env[var_name]
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return the requested shell variable with the get_var() method (Naked.toolshed.shell).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ get_split_var_list method ] (list of strings)
|
||||||
|
# return a list of strings split by OS dependent separator from the shell variable assigment string
|
||||||
|
# if the variable name is not in the environment list, returns an empty list
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def get_split_var_list(self, var_name):
|
||||||
|
try:
|
||||||
|
if var_name in self.vars:
|
||||||
|
return self.env[var_name].split(os.pathsep)
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return environment variable list with the get_split_var_list() method (Naked.toolshed.shell).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
||||||
|
# e = Environment()
|
||||||
|
# pathlist = e.get_split_var_list("PATH")
|
||||||
|
# for item in pathlist:
|
||||||
|
# print(item)
|
@ -0,0 +1,34 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
from Naked.toolshed.system import cwd
|
||||||
|
import Naked.toolshed.python as py
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
class StateObject:
|
||||||
|
def __init__(self):
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
self.py2 = py.is_py2() #truth test Python 2 interpreter
|
||||||
|
self.py3 = py.is_py3() #truth test Python 3 interpreter
|
||||||
|
self.py_major = py.py_major_version() #Python major version
|
||||||
|
self.py_minor = py.py_minor_version() #Python minor version
|
||||||
|
self.py_patch = py.py_patch_version() #Python patch version
|
||||||
|
self.os = sys.platform #user operating system
|
||||||
|
self.cwd = cwd() #current (present) working directory
|
||||||
|
self.parent_dir = os.pardir
|
||||||
|
self.default_path = os.defpath
|
||||||
|
self.user_path = os.path.expanduser("~")
|
||||||
|
self.string_encoding = sys.getdefaultencoding()
|
||||||
|
self.file_encoding = sys.getfilesystemencoding()
|
||||||
|
self.hour = now.hour
|
||||||
|
self.min = now.minute
|
||||||
|
self.year = now.year
|
||||||
|
self.day = now.day
|
||||||
|
self.month = now.month
|
||||||
|
self.second = now.second
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
@ -0,0 +1,553 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# FILE & DIRECTORY PATHS
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ filename function ] (string)
|
||||||
|
# returns file name from a file path (including the file extension)
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_filename
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def filename(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.basename(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return base filename from filename() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ file_extension function ] (string)
|
||||||
|
# returns file extension from a filepath
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_file_extension
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def file_extension(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.splitext(filepath)[1]
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return file extension with file_extension() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ directory function ] (string)
|
||||||
|
# returns directory path to the filepath
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_dir_path
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def directory(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.dirname(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return directory path to file with directory() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ make_path function ] (string)
|
||||||
|
# returns OS independent file path from tuple of path components
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_make_filepath
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def make_path(*path_list):
|
||||||
|
try:
|
||||||
|
return os.path.join(*path_list)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to make OS independent path with the make_path() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ currentdir_to_basefile decorator function ] (returns decorated original function)
|
||||||
|
# concatenates the absolute working directory path to the basename of file in the first parameter of the undecorated function
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_add_currentdir_path_to_basefile
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def currentdir_to_basefile(func):
|
||||||
|
try:
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(file_name, *args, **kwargs):
|
||||||
|
current_directory = os.getcwd() #get current working directory path
|
||||||
|
full_path = os.path.join(current_directory, file_name) # join cwd path to the filename for full path
|
||||||
|
return func(full_path, *args, **kwargs) #return the original function with the full path to file as first argument
|
||||||
|
return wrapper
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with the currentdir_to_basefile() decorator function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ currentdir_firstparam decorator function ] (returns decorated original function)
|
||||||
|
# adds the current working directory as the first function parameter of the decorated function
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_add_currentdir_path_first_arg
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def currentdir_firstparam(func):
|
||||||
|
try:
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(dir="", *args, **kwargs):
|
||||||
|
current_directory = os.getcwd()
|
||||||
|
return func(current_directory, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with the currentdir_firstargument() decorator function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ currentdir_lastargument decorator function ] (returns decorated original function)
|
||||||
|
# adds the current working directory as the last function parameter of the decorated function
|
||||||
|
# Note: you cannot use other named arguments in the original function with this decorator
|
||||||
|
# Note: the current directory argument in the last position must be named current_dir
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_add_currentdir_last_arg
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def currentdir_lastparam(func):
|
||||||
|
try:
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
the_cwd = os.getcwd()
|
||||||
|
return func(*args, current_dir=the_cwd)
|
||||||
|
return wrapper
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with the currentdir_lastargument() decorator function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ fullpath function ] (string)
|
||||||
|
# returns the absolute path to a file that is in the current working directory
|
||||||
|
# file_name = the basename of the file in the current working directory
|
||||||
|
# Example usage where test.txt is in working directory:
|
||||||
|
# filepath = fullpath("test.txt")
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_full_path_to_file
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
@currentdir_to_basefile # current directory decorator - adds the directory path up to the filename to the basefile name argument to original function
|
||||||
|
def fullpath(file_name):
|
||||||
|
try:
|
||||||
|
return file_name
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return absolute path to the file with the fullpath() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ cwd function ] (string)
|
||||||
|
# returns the current working directory path
|
||||||
|
# does not need to be called with an argument, the decorator assigns it
|
||||||
|
# Example usage:
|
||||||
|
# current_dir = cwd()
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_cwd_path
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
@currentdir_firstparam
|
||||||
|
def cwd(dir=""):
|
||||||
|
try:
|
||||||
|
return dir
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return the current working directory with the cwd() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# DIRECTORY WRITES
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## TODO: add tests
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ make_dirs function ] (--none--)
|
||||||
|
# make a new directory path (recursive if multiple levels of depth) if it
|
||||||
|
# DOES NOT already exist
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def make_dirs(dirpath):
|
||||||
|
try:
|
||||||
|
import os
|
||||||
|
import errno
|
||||||
|
os.makedirs(dirpath)
|
||||||
|
return True
|
||||||
|
except OSError as ose:
|
||||||
|
if ose.errno != errno.EEXIST: # directory already exists
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: Could not write the directory path passed as an argument to the make_dirs() function (Naked.toolshed.system).")
|
||||||
|
raise ose
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# FILE & DIRECTORY TESTING
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ file_exists function ] (boolean)
|
||||||
|
# return boolean for existence of file in specified path
|
||||||
|
# Tests: test_SYSTEM.py :: test_file_exists
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def file_exists(filepath):
|
||||||
|
try:
|
||||||
|
if os.path.exists(filepath) and os.path.isfile(filepath): # test that exists and is a file
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with test for the presence of the file with the file_exists() method (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_file function ] (boolean)
|
||||||
|
# returns boolean for determination of whether filepath is a file
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_is_file, test_sys_is_file_missing_file,
|
||||||
|
# test_sys_is_file_when_dir
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_file(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.isfile(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with test for file with the is_file() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ dir_exists function ] (boolean)
|
||||||
|
# return boolean for existence of directory in specified path
|
||||||
|
# Tests: test_SYSTEM.py :: test_dir_exists, test_dir_exists_missing_dir
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def dir_exists(dirpath):
|
||||||
|
try:
|
||||||
|
if os.path.exists(dirpath) and os.path.isdir(dirpath): # test that exists and is a directory
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with test for directory with the dir_exists() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_dir function ] (boolean)
|
||||||
|
# returns boolean for determination of whether dirpath is a directory
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_dir_is_dir, test_sys_dir_is_dir_when_file,
|
||||||
|
# test_sys_dir_is_dir_when_missing
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_dir(dirpath):
|
||||||
|
try:
|
||||||
|
return os.path.isdir(dirpath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: error with test for directory with the is_dir() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# FILE METADATA
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ filesize function ] (int)
|
||||||
|
# return file size in bytes
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_meta_file_size
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def file_size(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.getsize(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return file size with the file_size() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ file_mod_time function ] (string)
|
||||||
|
# return the last file modification date/time
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_meta_file_mod
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def file_mod_time(filepath):
|
||||||
|
try:
|
||||||
|
import time
|
||||||
|
return time.ctime(os.path.getmtime(filepath))
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return file modification data with the file_mod_time() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# FILE LISTINGS
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ list_all_files function ] (list)
|
||||||
|
# returns a list of all files in developer specified directory
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_list_all_files, test_sys_list_all_files_emptydir
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def list_all_files(dir):
|
||||||
|
try:
|
||||||
|
filenames = [name for name in os.listdir(dir) if os.path.isfile(os.path.join(dir, name))]
|
||||||
|
return filenames
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to generate directory file list with the list_all_files() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ list_filter_files function ] (list)
|
||||||
|
# returns a list of files filtered by developer defined file extension in developer defined directory
|
||||||
|
# Usage example:
|
||||||
|
# filenames = list_filter_files("py", "tests")
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_list_filter_files, test_sys_list_filter_files_nomatch
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def list_filter_files(extension_filter, dir):
|
||||||
|
try:
|
||||||
|
if not extension_filter.startswith("."):
|
||||||
|
extension_filter = "." + extension_filter
|
||||||
|
filenames = [name for name in os.listdir(dir) if name.endswith(extension_filter)]
|
||||||
|
return filenames
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return list of filtered files with the list_filter_files() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ list_all_files_cwd function ] (list)
|
||||||
|
# returns a list of all files in the current working directory
|
||||||
|
# Note: does not require argument, the decorator assigns the cwd
|
||||||
|
# Usage example:
|
||||||
|
# file_list = list_all_files_cwd()
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_list_all_files_cwd
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
@currentdir_firstparam
|
||||||
|
def list_all_files_cwd(dir=""):
|
||||||
|
try:
|
||||||
|
return list_all_files(dir)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return list of all files in current working directory with the list_all_files_cwd() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ list_filter_files_cwd function ] (list)
|
||||||
|
# returns a list of all files in the current working directory filtered by developer specified file extension
|
||||||
|
# Note: do not specify the second argument, decorator assigns it
|
||||||
|
# Usage example:
|
||||||
|
# file_list = list_filter_files_cwd(".py")
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_filter_files_cwd, test_sys_filter_files_cwd_nomatch
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
@currentdir_lastparam
|
||||||
|
def list_filter_files_cwd(extension_filter, current_dir=""):
|
||||||
|
try:
|
||||||
|
return list_filter_files(extension_filter, current_dir)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return list of filtered files in current working directory with the list_filter_files_cwd() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ list_match_files function ] (list)
|
||||||
|
# returns a list of all files that match the developer specified wildcard match pattern
|
||||||
|
# can optionally specify return of full path to the files (rather than relative path from cwd) by setting full_path to True
|
||||||
|
# Usage examples:
|
||||||
|
# file_list = list_match_files("*.py")
|
||||||
|
# file_list_fullpath = list_match_files("*.py", True)
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_match_files, test_sys_match_files_fullpath
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def list_match_files(match_pattern, full_path = False):
|
||||||
|
try:
|
||||||
|
from glob import glob
|
||||||
|
filenames = glob(match_pattern)
|
||||||
|
if full_path:
|
||||||
|
filenames_fullpath = []
|
||||||
|
cwd = os.getcwd()
|
||||||
|
for name in filenames:
|
||||||
|
name = os.path.join(cwd, name) #make the full path to the file
|
||||||
|
filenames_fullpath.append(name) #add to the new list
|
||||||
|
return filenames_fullpath #then return that list
|
||||||
|
else:
|
||||||
|
return filenames
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return list of matched files with the list_match_files() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# SYMBOLIC LINK TESTING
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ is_link function ] (boolean)
|
||||||
|
# return boolean indicating whether the path is a symbolic link
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def is_link(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.islink(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to determine whether path is a symbolic link with the is_link() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ real_path function ] (string)
|
||||||
|
# return the real file path pointed to by a symbolic link
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def real_path(filepath):
|
||||||
|
try:
|
||||||
|
return os.path.realpath(filepath)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to return real path for symbolic link with the real_path() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# DATA STREAMS
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stdout function ]
|
||||||
|
# print to std output stream
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stdout(text):
|
||||||
|
try:
|
||||||
|
print(text)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard output stream with the stdout() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stdout_xnl function ]
|
||||||
|
# print to std output stream without a newline
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stdout_xnl(text):
|
||||||
|
try:
|
||||||
|
sys.stdout.write(text)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard output stream with the stdout_xnl() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stdout_iter function ]
|
||||||
|
# print items in an iterable to the standard output stream with newlines after each string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stdout_iter(iter):
|
||||||
|
try:
|
||||||
|
for x in iter:
|
||||||
|
stdout(x)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard output stream with the stdout_iter() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stdout_iter_xnl function ]
|
||||||
|
# print items in an iterable to the standard output stream without newlines after each string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stdout_iter_xnl(iter):
|
||||||
|
try:
|
||||||
|
for x in iter:
|
||||||
|
stdout_xnl(x)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard output stream with the stdout_iter() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stderr function ]
|
||||||
|
# print to std error stream
|
||||||
|
# optionally (i.e. if exit = nonzero integer) permits exit from application with developer defined exit code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stderr(text, exit=0):
|
||||||
|
try:
|
||||||
|
sys.stderr.write(text + "\n")
|
||||||
|
if exit:
|
||||||
|
raise SystemExit(exit)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard error stream with the stderr() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ stderr_xnl function ]
|
||||||
|
# print to the standard error stream without a newline character after the `text` string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def stderr_xnl(text, exit=0):
|
||||||
|
try:
|
||||||
|
sys.stderr.write(text)
|
||||||
|
if exit:
|
||||||
|
raise SystemExit(exit)
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to print to the standard error stream with the stderr() function (Naked.toolshed.system).")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# APPLICATION CONTROL
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ exit_with_status function ]
|
||||||
|
# application exit with developer specified exit status code (default = 0)
|
||||||
|
# use an exit status integer argument
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_exit_with_code
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def exit_with_status(exit=0):
|
||||||
|
raise SystemExit(exit)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ exit_fail function ]
|
||||||
|
# application exit with status code 1
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_exit_failure
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def exit_fail():
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ exit_success function]
|
||||||
|
# application exit with status code 0
|
||||||
|
# Tests: test_SYSTEM.py :: test_sys_exit_success
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def exit_success():
|
||||||
|
raise SystemExit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
||||||
|
# #------------------------------------------------------------------------------
|
||||||
|
# # Standard Output Tests
|
||||||
|
# #------------------------------------------------------------------------------
|
||||||
|
# stdout("This is a test")
|
||||||
|
# for x in range(10):
|
||||||
|
# stdout_xnl(str(x) + " ")
|
||||||
|
# list_ten = ['10% ', '20% ', '30% ', '40% ', '50% ', '60% ', '70% ', '80% ', '90% ', '100%']
|
||||||
|
# stdout_iter(list_ten)
|
||||||
|
# #------------------------------------------------------------------------------
|
||||||
|
# # Standard Error Tests
|
||||||
|
# #------------------------------------------------------------------------------
|
||||||
|
# stderr("This is a test")
|
||||||
|
# stderr("This is a test", 1) #exit with status code 1
|
@ -0,0 +1,992 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from Naked.settings import debug as DEBUG_FLAG
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ NakedObject class ]]
|
||||||
|
# A generic Python object
|
||||||
|
# Assigns object attributes by key name in the dictionary argument to the constructor
|
||||||
|
# The methods are inherited by other mutable Naked object extension types
|
||||||
|
# Attribute accessors: hasattr, getattr, setattr, delattr
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class NakedObject(object):
|
||||||
|
# initialize with an attributes dictionary {attribute_name: attribute_value}
|
||||||
|
def __init__(self, attributes={}, naked_type='NakedObject'):
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(self, key, attributes[key])
|
||||||
|
setattr(self, '_naked_type_', naked_type) # maintain an attribute to keep track of the extension type
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _getAttributeDict method ] (dictionary)
|
||||||
|
# returns a dictionary of the NakedObject instance attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _getAttributeDict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _equal_type method ] (boolean)
|
||||||
|
# returns boolean for type of instance == type of test parameter instance
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _equal_type(self, other_obj):
|
||||||
|
return type(self) == type(other_obj)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _equal_attributes metod ] (method)
|
||||||
|
# returns boolean for instance.__dict__ == test parameter .__dict__ (attribute comparison)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _equal_attributes(self, other_obj):
|
||||||
|
return self.__dict__ == other_obj.__dict__
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# == overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __eq__(self, other_obj):
|
||||||
|
return self.equals(other_obj)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# != overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __ne__(self, other_obj):
|
||||||
|
result = self.equals(other_obj)
|
||||||
|
if result:
|
||||||
|
return False # reverse result of the equals method
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ equals method ] (boolean)
|
||||||
|
# equality testing based on type and attributes
|
||||||
|
# **NEED TO OVERRIDE IN CLASSES THAT INHERIT
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def equals(self, other_obj):
|
||||||
|
return self._equal_type(other_obj) and self._equal_attributes(other_obj)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ type method ] (string)
|
||||||
|
# returns the Naked type extension string that is set in the constructor for each object type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def type(self):
|
||||||
|
if hasattr(self, '_naked_type_'):
|
||||||
|
return self._naked_type_
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XDict class ]]
|
||||||
|
# An inherited extension to the dictionary type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XDict(dict, NakedObject):
|
||||||
|
def __init__(self, dict_obj, attributes={}, naked_type='XDict'):
|
||||||
|
dict.__init__(self, dict_obj)
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XDict Operator Overloads
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# + overload
|
||||||
|
# overwrites existing keys with key:value pairs from new dictionaries if they are the same keys
|
||||||
|
# returns the updated XDict object
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __add__(self, other_dict):
|
||||||
|
try:
|
||||||
|
self.update(other_dict)
|
||||||
|
if hasattr(other_dict, '_naked_type_') and (getattr(other_dict, '_naked_type_') == 'XDict'):
|
||||||
|
attr_dict = other_dict._getAttributeDict() # get the attributes from the parameter XDict and add to new XDict
|
||||||
|
if len(attr_dict) > 0:
|
||||||
|
for key in attr_dict:
|
||||||
|
setattr(self, key, attr_dict[key])
|
||||||
|
return self
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to combine XDict with parameter provided (Naked.toolshed.types.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# +- overload
|
||||||
|
# overwrites existing keys with another_dict (right sided argument) keys if they are the same keys
|
||||||
|
# returns the updated XDict object
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __iadd__(self, other_dict):
|
||||||
|
try:
|
||||||
|
self.update(other_dict)
|
||||||
|
if hasattr(other_dict, '_naked_type_') and (getattr(other_dict, '_naked_type_') == 'XDict'):
|
||||||
|
attr_dict = other_dict._getAttributeDict() # get the attributes from the parameter XDict and add to new XDict
|
||||||
|
if len(attr_dict) > 0:
|
||||||
|
for key in attr_dict:
|
||||||
|
setattr(self, key, attr_dict[key])
|
||||||
|
return self
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to combine XDict with parameter provided (Naked.toolshed.types.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# == overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __eq__(self, other_obj):
|
||||||
|
return self.equals(other_obj)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# != overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __ne__(self, other_obj):
|
||||||
|
result = self.equals(other_obj)
|
||||||
|
if result:
|
||||||
|
return False # reverse result of the equals method
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ equals method ] (boolean)
|
||||||
|
# tests for equality of the XDict (type, attributes, dictionary equality)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def equals(self, other_obj):
|
||||||
|
if self._equal_type(other_obj) and self._equal_attributes(other_obj):
|
||||||
|
if dict(self) == dict(other_obj):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XDict Value Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ conditional_map_to_vals method ] (XDict)
|
||||||
|
# returns the original XDict with values that meet True condition in `conditional_function`
|
||||||
|
# modified as per the `mapped_function` with single value argument call
|
||||||
|
# Test: test_xdict_conditional_map
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def conditional_map_to_vals(self, conditional_function, mapped_function):
|
||||||
|
for key, value in self.xitems():
|
||||||
|
if conditional_function(key):
|
||||||
|
self[key] = mapped_function(value)
|
||||||
|
return self
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ map_to_vals method ] (XDict)
|
||||||
|
# returns the original XDict with all values modified as per the `mapped_function`
|
||||||
|
# Test: test_xdict_map_to_vals
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def map_to_vals(self, mapped_function):
|
||||||
|
# return XDict( zip(self, map(mapped_function, self.values())), self._getAttributeDict() ) - slower in Py2
|
||||||
|
for key, value in self.xitems():
|
||||||
|
self[key] = mapped_function(value)
|
||||||
|
return self
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ val_xlist method ] (XList)
|
||||||
|
# return an XList of the values in the XDict
|
||||||
|
# Test: test_xdict_val_xlist
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def val_xlist(self):
|
||||||
|
return XList(self.values(), self._getAttributeDict())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ max_val method ] (tuple of maximum value and associated key)
|
||||||
|
# Test: test_xdict_max_val, test_xdict_max_val_strings (strings are alphabetic if not numerals)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def max_val(self):
|
||||||
|
return max(zip(self.values(), self.keys()))
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ min_val method ] (tuple of minimum value and associated key)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def min_val(self):
|
||||||
|
return min(zip(self.values(), self.keys()))
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ sum_vals method ] (numeric return type dependent upon original value type)
|
||||||
|
# returns sum of all values in the dictionary
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def sum_vals(self):
|
||||||
|
return sum(self.values())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ val_count method ] (integer)
|
||||||
|
# returns an integer value for the total count of `value_name` in the dictionary values
|
||||||
|
# Case sensitive test if comparing strings
|
||||||
|
# Tests: test_xdict_val_count_string, test_xdict_val_count_integer
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def val_count(self, value_name):
|
||||||
|
count = 0
|
||||||
|
for test_value in self.values():
|
||||||
|
if value_name == test_value:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ value_count_ci method ] (integer)
|
||||||
|
# returns an integer value for the total count of case insensitive `value_name`
|
||||||
|
# strings/char in the dictionary values. Can include non-string types (ignores them)
|
||||||
|
# Test: test_xdict_val_count_ci
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def val_count_ci(self, value_name):
|
||||||
|
count = 0
|
||||||
|
for test_value in self.values():
|
||||||
|
try:
|
||||||
|
if value_name.lower() in test_value.lower():
|
||||||
|
count += 1
|
||||||
|
except AttributeError: # the test_value was not a string, catch exception and continue count attempt
|
||||||
|
continue
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XDict Key Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ difference method ] (difference set of keys)
|
||||||
|
# definition: keys that are included in self, but not in `another_dict`
|
||||||
|
# Tests: test_xdict_key_difference, test_xdict_key_difference_when_none_present
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def difference(self, another_dict):
|
||||||
|
return set(self.keys()) - set(another_dict.keys())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ intersection method ] (intersection set of keys)
|
||||||
|
# definition: keys that are included in both self and `another_dict`
|
||||||
|
# Tests: test_xdict_key_intersection, test_xdict_key_intersection_when_none_present
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def intersection(self, another_dict):
|
||||||
|
return set(self.keys()) & set(another_dict.keys())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ key_xlist method ] (XList)
|
||||||
|
# returns an XList of the keys in the XDict
|
||||||
|
# Test: test_xdict_key_xlist
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def key_xlist(self):
|
||||||
|
return XList(self.keys(), self._getAttributeDict())
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ random method ] (dictionary)
|
||||||
|
# return new Python dictionary with single, random key:value pair
|
||||||
|
# Test: test_xdict_key_random
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def random(self):
|
||||||
|
import random
|
||||||
|
from Naked.toolshed.python import py_major_version
|
||||||
|
random_key_list = random.sample(self.keys(), 1)
|
||||||
|
the_key = random_key_list[0]
|
||||||
|
return {the_key: self[the_key]}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ random_sample method ] (dictionary)
|
||||||
|
# return new Python dictionary with `number_of_items` random key:value pairs
|
||||||
|
# Test: test_xdict_key_random_sample
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def random_sample(self, number_of_items):
|
||||||
|
import random
|
||||||
|
random_key_list = random.sample(self.keys(), number_of_items)
|
||||||
|
new_dict = {}
|
||||||
|
for item in random_key_list:
|
||||||
|
new_dict[item] = self[item]
|
||||||
|
return new_dict
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xitems method ] (tuple)
|
||||||
|
# Generator method that returns tuples of every key, value in dictionary
|
||||||
|
# uses appropriate method from Python 2 and 3 interpreters
|
||||||
|
# Test: test_xdict_xitems
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xitems(self):
|
||||||
|
from Naked.toolshed.python import py_major_version
|
||||||
|
if py_major_version() > 2:
|
||||||
|
return self.items()
|
||||||
|
else:
|
||||||
|
return self.iteritems()
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XList class ]]
|
||||||
|
# An inherited extension to the list object that permits attachment of attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XList(list, NakedObject):
|
||||||
|
def __init__(self, list_obj, attributes={}, naked_type='XList'):
|
||||||
|
list.__init__(self, list_obj)
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Operator Overloads
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# + operator overload
|
||||||
|
# extends XList with one or more other lists (`*other_lists`)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __add__(self, *other_lists):
|
||||||
|
try:
|
||||||
|
for the_list in other_lists:
|
||||||
|
# add attributes if it is an XList
|
||||||
|
if hasattr(the_list, '_naked_type_') and (getattr(the_list, '_naked_type_') == 'XList'):
|
||||||
|
attr_dict = the_list._getAttributeDict() # get XList attribute dictionary
|
||||||
|
if len(attr_dict) > 0:
|
||||||
|
for key in attr_dict:
|
||||||
|
setattr(self, key, attr_dict[key])
|
||||||
|
# extend the XList items
|
||||||
|
self.extend(the_list)
|
||||||
|
return self
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to combine XList with parameter provided (Naked.toolshed.types.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# += overload
|
||||||
|
# extends XList with one other list (`another_list`)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __iadd__(self, another_list):
|
||||||
|
try:
|
||||||
|
#add attributes if it is an XList
|
||||||
|
if hasattr(another_list, '_naked_type_') and (getattr(another_list, '_naked_type_') == 'XList'):
|
||||||
|
attr_dict = another_list._getAttributeDict() # get XList attribute dictionary
|
||||||
|
if len(attr_dict) > 0:
|
||||||
|
for key in attr_dict:
|
||||||
|
setattr(self, key, attr_dict[key])
|
||||||
|
# extend the XList items
|
||||||
|
self.extend(another_list)
|
||||||
|
return self
|
||||||
|
except Exception as e:
|
||||||
|
if DEBUG_FLAG:
|
||||||
|
sys.stderr.write("Naked Framework Error: unable to combine XList with parameter provided (Naked.toolshed.types.py)")
|
||||||
|
raise e
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# == overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __eq__(self, other_obj):
|
||||||
|
return self.equals(other_obj)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# != overload
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def __ne__(self, other_obj):
|
||||||
|
result = self.equals(other_obj)
|
||||||
|
if result:
|
||||||
|
return False # reverse result of the equals method
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ equals method ] (boolean)
|
||||||
|
# tests for equality of the XList (type, attributes, list equality)
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def equals(self, other_obj):
|
||||||
|
if self._equal_type(other_obj) and self._equal_attributes(other_obj):
|
||||||
|
if list(self) == list(other_obj):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList String Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ join method ] (string)
|
||||||
|
# Concatenate strings in the list and return
|
||||||
|
# Default separator between string list values is an empty string
|
||||||
|
# Pass separator character(s) as an argument to the method
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def join(self, separator=""):
|
||||||
|
return separator.join(self)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ postfix method ] (list of strings)
|
||||||
|
# Append a string to each list item string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def postfix(self, after):
|
||||||
|
return [ "".join([x, after]) for x in self ]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ prefix method ] (list of strings)
|
||||||
|
# Prepend a string to each list item string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def prefix(self, before):
|
||||||
|
return [ "".join([before, x]) for x in self ]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ surround method ] (list of strings)
|
||||||
|
# Surround each list item string with a before and after string argument passed to the method
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def surround(self, before, after=""):
|
||||||
|
if after == "":
|
||||||
|
after = before
|
||||||
|
return [ "".join([before, x, after]) for x in self ]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Numeric Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ max method ] (list dependent type, single value)
|
||||||
|
# return maximum value from the list items
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def max(self):
|
||||||
|
return max(self)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ min method ] (list dependent type, single value)
|
||||||
|
# return minimum value from the list items
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def min(self):
|
||||||
|
return min(self)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ sum method ] (list dependent type, single value)
|
||||||
|
# return the sum of all list items
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def sum(self):
|
||||||
|
return sum(self)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Data Management Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ count_duplicates method ] (integer)
|
||||||
|
# returns an integer count of number of duplicate values
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def count_duplicates(self):
|
||||||
|
return len(self) - len(set(self))
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ remove_duplicates ] (XList)
|
||||||
|
# returns a new XList with duplicates removed
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def remove_duplicates(self):
|
||||||
|
return XList( set(self), self._getAttributeDict() )
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ difference method ] (set)
|
||||||
|
# returns a set containing items in XList that are not contained in `another_list`
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def difference(self, another_list):
|
||||||
|
return set(self) - set(another_list)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ intersection method ] (set)
|
||||||
|
# returns a set containing items that are in both XList and `another_list`
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def intersection(self, another_list):
|
||||||
|
return set(self) & set(another_list)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Function Mapping Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ map_to_items method ] (XList)
|
||||||
|
# returns original XList with modification of each item based upon `mapped_function`
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def map_to_items(self, mapped_function):
|
||||||
|
# return XList( map(mapped_function, self), self._getAttributeDict() ) - slower
|
||||||
|
for index, item in enumerate(self):
|
||||||
|
self[index] = mapped_function(item)
|
||||||
|
return self
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ conditional_map_to_items method ] (XList)
|
||||||
|
# returns original XList with modification of items that meet True condition in
|
||||||
|
# `conditional_function` with change performed as defined in `mapped_function`
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def conditional_map_to_items(self, conditional_function, mapped_function):
|
||||||
|
for index, item in enumerate(self):
|
||||||
|
if conditional_function(item):
|
||||||
|
self[index] = mapped_function(item)
|
||||||
|
return self
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Descriptive Stats Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ count_ci method ] (integer)
|
||||||
|
# returns an integer count of the number of case-insensitive items that match `test_string`
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def count_ci(self, test_string):
|
||||||
|
count = 0
|
||||||
|
for item in self:
|
||||||
|
try:
|
||||||
|
if test_string.lower() in item.lower():
|
||||||
|
count += 1
|
||||||
|
except AttributeError: # the test_value was not a string, catch exception and continue count attempt
|
||||||
|
continue
|
||||||
|
return count
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ random method ] (list)
|
||||||
|
# returns a single item list with a random element from the original XList
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def random(self):
|
||||||
|
import random
|
||||||
|
return random.choice(self)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ random_sample method ] (list)
|
||||||
|
# returns a list with one or more random items from the original XList
|
||||||
|
# number of items determined by the `number_of_items` argument
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def random_sample(self, number_of_items):
|
||||||
|
import random
|
||||||
|
return random.sample(self, number_of_items)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ shuffle method ] (XList)
|
||||||
|
# randomly shuffle the contents of the list
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def shuffle(self):
|
||||||
|
import random
|
||||||
|
random.shuffle(self)
|
||||||
|
return self
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Match Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ wildcard_match method ] (list)
|
||||||
|
# returns a list of items that match the `wildcard` argument
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def wildcard_match(self, wildcard):
|
||||||
|
if hasattr(self, 'nkd_fnmatchcase'):
|
||||||
|
fnmatchcase = self.nkd_fnmatchcase
|
||||||
|
else:
|
||||||
|
from fnmatch import fnmatchcase
|
||||||
|
self.nkd_fnmatchcase = fnmatchcase
|
||||||
|
return [ x for x in self if fnmatchcase(x, wildcard) ]
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ multi_wildcard_match method ] (list)
|
||||||
|
# returns a list of items that match one or more | separated wildcards passed as string
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def multi_wildcard_match(self, wildcards):
|
||||||
|
if hasattr(self, 'nkd_fnmatchcase'):
|
||||||
|
fnmatchcase = self.nkd_fnmatchcase
|
||||||
|
else:
|
||||||
|
from fnmatch import fnmatchcase
|
||||||
|
self.nkd_fnmatchcase = fnmatchcase
|
||||||
|
wc_list = wildcards.split('|')
|
||||||
|
return_list = []
|
||||||
|
for wc in wc_list:
|
||||||
|
temp_list = [ x for x in self if fnmatchcase(x, wc) ]
|
||||||
|
for result in temp_list:
|
||||||
|
return_list.append(result)
|
||||||
|
return return_list
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# XList Cast Methods
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xset method ] (XSet)
|
||||||
|
# return an XSet with unique XList item values and XList attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xset(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XSet(set(self), attr_dict)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xfset method ] (XFSet)
|
||||||
|
# return an XFSet with unique XList item values and XList attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xfset(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XFSet(set(self), attr_dict)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ xtuple method ] (XTuple)
|
||||||
|
# returns an XTuple with XList item values and XList attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def xtuple(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XTuple(tuple(self), attr_dict)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XMaxHeap class ]]
|
||||||
|
# max heap queue
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
from heapq import heappush, heappop
|
||||||
|
class XMaxHeap(NakedObject):
|
||||||
|
def __init__(self, attributes={}, naked_type='XMaxHeap'):
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
self._queue = []
|
||||||
|
self._index = 0
|
||||||
|
|
||||||
|
# length of the queue
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
# O(log n) complexity
|
||||||
|
def push(self, the_object, priority):
|
||||||
|
heappush(self._queue, (-priority, self._index, the_object))
|
||||||
|
self._index += 1
|
||||||
|
|
||||||
|
# O(log n) complexity
|
||||||
|
def pop(self):
|
||||||
|
if self._queue:
|
||||||
|
return heappop(self._queue)[-1]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# push new object and return the highest priority object
|
||||||
|
def pushpop(self, the_object, priority):
|
||||||
|
heappush(self._queue, (-priority, self._index, the_object))
|
||||||
|
self._index += 1
|
||||||
|
if self._queue:
|
||||||
|
return heappop(self._queue)[-1]
|
||||||
|
else:
|
||||||
|
return None # return None if the queue is empty
|
||||||
|
|
||||||
|
# the length of the queue
|
||||||
|
def length(self):
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XMinHeap class ]]
|
||||||
|
# min heap queue
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
from heapq import heappush, heappop
|
||||||
|
class XMinHeap(NakedObject):
|
||||||
|
def __init__(self, attributes={}, naked_type='XMinHeap'):
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
self._queue = []
|
||||||
|
self._index = 0
|
||||||
|
|
||||||
|
|
||||||
|
# length of the queue
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
# O(log n) complexity
|
||||||
|
def push(self, the_object, priority):
|
||||||
|
heappush(self._queue, (priority, self._index, the_object))
|
||||||
|
self._index += 1
|
||||||
|
|
||||||
|
# O(log n) complexity
|
||||||
|
def pop(self):
|
||||||
|
if self._queue:
|
||||||
|
return heappop(self._queue)[-1]
|
||||||
|
else:
|
||||||
|
return None # return None if the queue is empty
|
||||||
|
|
||||||
|
# push new object and return the lowest priority object
|
||||||
|
def pushpop(self, the_object, priority):
|
||||||
|
heappush(self._queue, (priority, self._index, the_object))
|
||||||
|
self._index += 1
|
||||||
|
if self._queue:
|
||||||
|
return heappop(self._queue)[-1]
|
||||||
|
else:
|
||||||
|
return None #return None if the queue is empty
|
||||||
|
|
||||||
|
# the length of the queue
|
||||||
|
def length(self):
|
||||||
|
return len(self._queue)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XQueue class ]]
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
from collections import deque
|
||||||
|
class XQueue(deque, NakedObject):
|
||||||
|
def __init__(self, initial_iterable=[], attributes={}, max_length=10, naked_type='XQueue'):
|
||||||
|
deque.__init__(self, initial_iterable, max_length)
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XSet class ]]
|
||||||
|
# An inherited extension to the mutable set object that permits attribute assignment
|
||||||
|
# Inherits from set and from NakedObject (see methods in NakedObject at top of this module
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XSet(set, NakedObject):
|
||||||
|
def __init__(self, set_obj, attributes={}, naked_type='XSet'):
|
||||||
|
set.__init__(self, set_obj)
|
||||||
|
NakedObject.__init__(self, attributes, naked_type)
|
||||||
|
|
||||||
|
# += operator overload to extend the XSet with a second set
|
||||||
|
def __iadd__(self, another_set):
|
||||||
|
self.update(another_set)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def xlist(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XList(list(self), attr_dict)
|
||||||
|
|
||||||
|
def xfset(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XFSet(self, attr_dict)
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XFSet class ]]
|
||||||
|
# An inherited extension to the immutable frozenset object that permits attribute assignment
|
||||||
|
# Immutable so there is no setter method, attributes must be set in the constructor
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XFSet(frozenset):
|
||||||
|
def __new__(cls, the_set, attributes={}, naked_type="XFSet"):
|
||||||
|
set_obj = frozenset.__new__(cls, the_set)
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(set_obj, key, attributes[key])
|
||||||
|
setattr(set_obj, '_naked_type_', naked_type) # set the naked extension type as an attribute (NakedObject does this for mutable classes)
|
||||||
|
return set_obj
|
||||||
|
|
||||||
|
def _getAttributeDict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
def xlist(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XList(list(self), attr_dict, naked_type="XList")
|
||||||
|
|
||||||
|
def xset(self):
|
||||||
|
attr_dict = self._getAttributeDict()
|
||||||
|
return XSet(self, attr_dict, naked_type="XSet")
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ type method ] (string)
|
||||||
|
# returns the Naked type extension string that is set in the constructor for each object type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def type(self):
|
||||||
|
if hasattr(self, '_naked_type_'):
|
||||||
|
return self._naked_type_
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XString class ]]
|
||||||
|
# An inherited extension to the immutable string object that permits attribute assignment
|
||||||
|
# Immutable so there is no setter method, attributes must be set in the constructor
|
||||||
|
# Python 2: byte string by default, can cast to normalized UTF-8 with XString().unicode() method
|
||||||
|
# Python 3: string (that permits unicode) by default, can normalize with XString().unicode() method
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XString(str):
|
||||||
|
def __new__(cls, string_text, attributes={}, naked_type='XString'):
|
||||||
|
str_obj = str.__new__(cls, string_text)
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(str_obj, key, attributes[key])
|
||||||
|
setattr(str_obj, '_naked_type_', naked_type)
|
||||||
|
return str_obj
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _getAttributeDict method ] (dictionary)
|
||||||
|
# returns a dictionary of the XString instance attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _getAttributeDict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ type method ] (string)
|
||||||
|
# returns the Naked type extension string that is set in the constructor for each object type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def type(self):
|
||||||
|
if hasattr(self, '_naked_type_'):
|
||||||
|
return self._naked_type_
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
## TODO: see where + vs. join breakpoint becomes important
|
||||||
|
def concat(self, *strings):
|
||||||
|
str_list = []
|
||||||
|
for x in strings:
|
||||||
|
str_list.append(x)
|
||||||
|
return "".join(str_list)
|
||||||
|
|
||||||
|
# fastest substring search truth test
|
||||||
|
def contains(self, substring):
|
||||||
|
return substring in self
|
||||||
|
|
||||||
|
# split the string on one or more delimiters, return list
|
||||||
|
# if up to two chars, then uses str.split(), if more chars then use re.split
|
||||||
|
def xsplit(self, split_delimiter):
|
||||||
|
length = len(split_delimiter)
|
||||||
|
if length > 2:
|
||||||
|
import re
|
||||||
|
split_delimiter = "".join([ '[', split_delimiter, ']' ])
|
||||||
|
return re.split(split_delimiter, self)
|
||||||
|
elif length > 1:
|
||||||
|
delim2 = split_delimiter[1]
|
||||||
|
first_list = self.split(split_delimiter[0])
|
||||||
|
result_list = []
|
||||||
|
for item in first_list:
|
||||||
|
for subitem in item.split(delim2):
|
||||||
|
result_list.append(subitem)
|
||||||
|
return result_list
|
||||||
|
else:
|
||||||
|
return self.split(split_delimiter)
|
||||||
|
|
||||||
|
# split the string on one or more characters and return items in set
|
||||||
|
def xsplit_set(self, split_delimiter):
|
||||||
|
return set(self.xsplit(split_delimiter))
|
||||||
|
|
||||||
|
# str begins with substring - faster than str.startswith()
|
||||||
|
def begins(self, begin_string):
|
||||||
|
return begin_string in self[0:len(begin_string)]
|
||||||
|
|
||||||
|
# str ends with substring - faster than str.endswith()
|
||||||
|
def ends(self, end_string):
|
||||||
|
return end_string in self[-len(end_string):]
|
||||||
|
|
||||||
|
# case sensitive wildcard match on the XString (boolean returned)
|
||||||
|
def wildcard_match(self, wildcard):
|
||||||
|
from fnmatch import fnmatchcase
|
||||||
|
return fnmatchcase(self, wildcard)
|
||||||
|
|
||||||
|
# convert string to normalized UTF-8 in Python 2 and 3 (##TODO: convert to XUnicode with attributes?)
|
||||||
|
def unicode(self):
|
||||||
|
from sys import version_info
|
||||||
|
from unicodedata import normalize
|
||||||
|
if version_info[0] == 2:
|
||||||
|
return normalize('NFKD', self.decode('UTF-8'))
|
||||||
|
else:
|
||||||
|
return normalize('NFKD', self)
|
||||||
|
|
||||||
|
|
||||||
|
# this version works
|
||||||
|
class XUnicode:
|
||||||
|
def __init__(self, string_text, attributes={}, naked_type='XUnicode'):
|
||||||
|
import sys
|
||||||
|
import unicodedata
|
||||||
|
norm_text = unicodedata.normalize('NFKD', string_text)
|
||||||
|
|
||||||
|
class XUnicode_2(unicode):
|
||||||
|
def __new__(cls, the_string_text, attributes={}, naked_type='XUnicode2'):
|
||||||
|
str_obj = unicode.__new__(cls, the_string_text)
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(str_obj, key, attributes[key])
|
||||||
|
setattr(str_obj, '_naked_type_', naked_type) # set the type to XUnicode2 for Py 2 strings
|
||||||
|
return str_obj
|
||||||
|
|
||||||
|
class XUnicode_3(str):
|
||||||
|
def __new__(cls, the_string_text, attributes={}, naked_type='XUnicode3'):
|
||||||
|
str_obj = str.__new__(cls, the_string_text)
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(str_obj, key, attributes[key])
|
||||||
|
setattr(str_obj, '_naked_type_', naked_type) # set the type to XUnicode3 for Py 3 strings
|
||||||
|
return str_obj
|
||||||
|
|
||||||
|
|
||||||
|
if sys.version_info[0] == 2:
|
||||||
|
self.obj = XUnicode_2(norm_text, attributes)
|
||||||
|
self.norm_unicode = norm_text
|
||||||
|
self.naked_u_string = self.obj.encode('utf-8') # utf-8 encoded byte string
|
||||||
|
elif sys.version_info[0] == 3:
|
||||||
|
self.naked_u_string = XUnicode_3(norm_text, attributes).encode('utf-8') # ?
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
# return self.naked_u_string
|
||||||
|
return self.obj
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self.naked_u_string
|
||||||
|
|
||||||
|
def __getattr__(self, the_attribute):
|
||||||
|
return self.obj.__dict__[the_attribute]
|
||||||
|
|
||||||
|
def __cmp__(self, other_string):
|
||||||
|
return hash(self.naked_u_string) == hash(other_string)
|
||||||
|
# TODO: add check for same attributes
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _getAttributeDict method ] (dictionary)
|
||||||
|
# returns a dictionary of the NakedObject instance attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _getAttributeDict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ type method ] (string)
|
||||||
|
# returns the Naked type extension string that is set in the constructor for each object type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def type(self):
|
||||||
|
if hasattr(self, '_naked_type_'):
|
||||||
|
return self._naked_type_
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [[ XTuple class ]]
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
class XTuple(tuple):
|
||||||
|
def __new__(cls, the_tuple, attributes={}, naked_type='XTuple'):
|
||||||
|
tup_obj = tuple.__new__(cls, the_tuple)
|
||||||
|
if len(attributes) > 0:
|
||||||
|
for key in attributes:
|
||||||
|
setattr(tup_obj, key, attributes[key])
|
||||||
|
setattr(tup_obj, '_naked_type_', naked_type)
|
||||||
|
return tup_obj
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ _getAttributeDict method ] (dictionary)
|
||||||
|
# returns a dictionary of the NakedObject instance attributes
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def _getAttributeDict(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# [ type method ] (string)
|
||||||
|
# returns the Naked type extension string that is set in the constructor for each object type
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
def type(self):
|
||||||
|
if hasattr(self, '_naked_type_'):
|
||||||
|
return self._naked_type_
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pass
|
||||||
|
# no = nobj({"version":"1.0.1", "test":"code"})
|
||||||
|
# print(no)
|
||||||
|
# print(no.version)
|
||||||
|
# print(no.test)
|
||||||
|
# nl = XList([1, 2, 3, 1, 2, 5], {"version":"1.0.1", "test":"code"})
|
||||||
|
# print(nl.count_duplicates())
|
||||||
|
# the_list = list(range(5000))
|
||||||
|
# nl = XList(the_list)
|
||||||
|
# nq = XPriorityQueue()
|
||||||
|
# nq.push('test', 5)
|
||||||
|
# nq.push('one', 3)
|
||||||
|
# nq.push('another', 4)
|
||||||
|
# print(nq.pop())
|
||||||
|
# print(nq.pop())
|
||||||
|
# print(nq.pop())
|
||||||
|
|
||||||
|
# nl = XList([2, 2, 2, 'another'], {'p': 'attribute'})
|
||||||
|
# print(nl)
|
||||||
|
# print(nl.count_item(2))
|
||||||
|
# nq = XQueue(nl, max_length=2)
|
||||||
|
# print(nq)
|
||||||
|
|
||||||
|
# xs = XSet({'test', 'true', 'false'}, {'bonus': 'candy', 'test': 'another'})
|
||||||
|
# xs += {'bogus', 'yep'}
|
||||||
|
# print(xs)
|
||||||
|
|
||||||
|
# xd = XDict({'test2': 0, 'is': 1}, {'a': '1', 'b': '2'})
|
||||||
|
# ad = {'test': 0, 'is': 2}
|
||||||
|
# ld = xd.intersection(ad)
|
||||||
|
# print(ld)
|
||||||
|
# xd = xd + ad + ld
|
||||||
|
# print(xd.map_to_vals(pr))
|
||||||
|
# print(xd.a)
|
||||||
|
# print(xd)
|
||||||
|
# print(xd.a)
|
||||||
|
# print(xd.min_val())
|
||||||
|
# print(xd.conditional_map_to_vals(matcher, resulter))
|
||||||
|
|
||||||
|
# nl = XList([ 'test.txt', 'bogus.txt', 'test.py', 'another.rb', 'est.doc', 'est.py' ])
|
||||||
|
# print(nl.multi_wildcard_match('*.py|*.txt|*.doc'))
|
||||||
|
|
||||||
|
# xstr = XString("Hey! Cœur It's Bengali ব য,\nand here is some more ২")
|
||||||
|
# ustr = xstr.unicode()
|
||||||
|
# print(isinstance(ustr, bytes))
|
||||||
|
# print(xstr)
|
||||||
|
|
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2017-2019 Ingy döt Net
|
||||||
|
Copyright (c) 2006-2016 Kirill Simonov
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
@ -0,0 +1,40 @@
|
|||||||
|
Metadata-Version: 2.1
|
||||||
|
Name: PyYAML
|
||||||
|
Version: 5.3
|
||||||
|
Summary: YAML parser and emitter for Python
|
||||||
|
Home-page: https://github.com/yaml/pyyaml
|
||||||
|
Author: Kirill Simonov
|
||||||
|
Author-email: xi@resolvent.net
|
||||||
|
License: MIT
|
||||||
|
Download-URL: https://pypi.org/project/PyYAML/
|
||||||
|
Platform: Any
|
||||||
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
|
Classifier: Intended Audience :: Developers
|
||||||
|
Classifier: License :: OSI Approved :: MIT License
|
||||||
|
Classifier: Operating System :: OS Independent
|
||||||
|
Classifier: Programming Language :: Cython
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 2
|
||||||
|
Classifier: Programming Language :: Python :: 2.7
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Classifier: Programming Language :: Python :: 3.5
|
||||||
|
Classifier: Programming Language :: Python :: 3.6
|
||||||
|
Classifier: Programming Language :: Python :: 3.7
|
||||||
|
Classifier: Programming Language :: Python :: 3.8
|
||||||
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||||
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||||
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||||
|
Classifier: Topic :: Text Processing :: Markup
|
||||||
|
|
||||||
|
YAML is a data serialization format designed for human readability
|
||||||
|
and interaction with scripting languages. PyYAML is a YAML parser
|
||||||
|
and emitter for Python.
|
||||||
|
|
||||||
|
PyYAML features a complete YAML 1.1 parser, Unicode support, pickle
|
||||||
|
support, capable extension API, and sensible error messages. PyYAML
|
||||||
|
supports standard YAML tags and provides Python-specific tags that
|
||||||
|
allow to represent an arbitrary Python object.
|
||||||
|
|
||||||
|
PyYAML is applicable for a broad range of tasks from complex
|
||||||
|
configuration files to object serialization and persistence.
|
||||||
|
|
@ -0,0 +1,40 @@
|
|||||||
|
PyYAML-5.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||||
|
PyYAML-5.3.dist-info/LICENSE,sha256=oq25yVm3l0lKDvgL32DiLbJ0nuPgwJCFVuPrVI-WfFY,1101
|
||||||
|
PyYAML-5.3.dist-info/METADATA,sha256=qeUmOwAa8JaIGvO7rR36WWVTAyA6OjLbv80lirmn4KI,1688
|
||||||
|
PyYAML-5.3.dist-info/RECORD,,
|
||||||
|
PyYAML-5.3.dist-info/WHEEL,sha256=xhTAlQlEzHPebOMFd-mhU4DM_uRYSil3AJ61fHfeWHQ,102
|
||||||
|
_yaml.cp37-win32.pyd,sha256=F0-gY63-wzdqJ9iYq4g-79svSHDp8bszK0-2jO4Gz3M,231424
|
||||||
|
yaml/__init__.py,sha256=d28oT0Lc_XTrMwdoyG_fnl7KLfZzPHAbewmELDwVkyY,13168
|
||||||
|
yaml/__pycache__/__init__.cpython-37.pyc,sha256=rCcebvcUhx0FVRO1KRSCSo-ALdzIYtjyTD62Tk52eJE,11721
|
||||||
|
yaml/__pycache__/composer.cpython-37.pyc,sha256=cOpEfSk4Fq-jFkPsieHN2kJcmcr2Vc6D8NVvYTPOE1o,3484
|
||||||
|
yaml/__pycache__/constructor.cpython-37.pyc,sha256=06qatUtXJKnpfjaT2pfJc-3XmcAlH_-6afhzOQo6Wsw,20074
|
||||||
|
yaml/__pycache__/cyaml.cpython-37.pyc,sha256=gyoH1zVnyV6BTcnDi4eM1HPYVt5XE-D4S4HZELEo4x4,3649
|
||||||
|
yaml/__pycache__/dumper.cpython-37.pyc,sha256=s3rc-ZaF4jFpa-1Oq4ZZVMKaxaWmyDbgvnUYVMjvdyI,2024
|
||||||
|
yaml/__pycache__/emitter.cpython-37.pyc,sha256=yn8qKLXPiFVHPkalKUOxvqt6c1GDpvDv64flUyA-fQw,25274
|
||||||
|
yaml/__pycache__/error.cpython-37.pyc,sha256=3nFo2OVqw39xQ2n8Xlb8YVDFtIk6QYnrQseaqzWqxgQ,2235
|
||||||
|
yaml/__pycache__/events.cpython-37.pyc,sha256=fd9PwYCVVRyLfNWoWJlFJTKKxwxqHJ9ptZz28J4pHwo,4001
|
||||||
|
yaml/__pycache__/loader.cpython-37.pyc,sha256=zMnbLaDnYxc_9DW3ath8WgEme7Cp8hYMWSVbgoYOD-0,2233
|
||||||
|
yaml/__pycache__/nodes.cpython-37.pyc,sha256=O1LH_BDmPYD52se6KfoBWlee7CPGtKI-kMv75EgnC00,1679
|
||||||
|
yaml/__pycache__/parser.cpython-37.pyc,sha256=mysaVAzDSPgpwNozJAHCBAIF2_ZwEkFAx5FDru0Ajvs,11829
|
||||||
|
yaml/__pycache__/reader.cpython-37.pyc,sha256=KNWgMf-RIz7QPmDwNDd-5d_ZO5ZIQbH66os3I0WMAlk,4442
|
||||||
|
yaml/__pycache__/representer.cpython-37.pyc,sha256=TuoUxqnBNU6w_RcE4nGkudplMHg0XvPc_PzOiXu5fEI,10065
|
||||||
|
yaml/__pycache__/resolver.cpython-37.pyc,sha256=eUi337rMdeLYgfEULvNcDQw9OvF-rXXnPKDA-p5Sroc,5415
|
||||||
|
yaml/__pycache__/scanner.cpython-37.pyc,sha256=zkmXD0FYIfQcr6rRNPsVvrKbNhpYNdgrXkDH-k1lNMw,25775
|
||||||
|
yaml/__pycache__/serializer.cpython-37.pyc,sha256=7V9jHBaequ3D4yC7q8Zg1EOOwnmBIlZGKzdbkJO04aY,3314
|
||||||
|
yaml/__pycache__/tokens.cpython-37.pyc,sha256=PEcIbV522wfYzKavUEos4QkZfFWxAhLmpF_9mmkZgjU,5178
|
||||||
|
yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883
|
||||||
|
yaml/constructor.py,sha256=4Pd2pssgKUMGLdS_kW6A4SwC7q0T13-sBrATvNQZCZ0,27321
|
||||||
|
yaml/cyaml.py,sha256=LiMkvchNonfoy1F6ec9L2BiUz3r0bwF4hympASJX1Ic,3846
|
||||||
|
yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837
|
||||||
|
yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006
|
||||||
|
yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533
|
||||||
|
yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445
|
||||||
|
yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061
|
||||||
|
yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440
|
||||||
|
yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495
|
||||||
|
yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794
|
||||||
|
yaml/representer.py,sha256=82UM3ZxUQKqsKAF4ltWOxCS6jGPIFtXpGs7mvqyv4Xs,14184
|
||||||
|
yaml/resolver.py,sha256=DJCjpQr8YQCEYYjKEYqTl0GrsZil2H4aFOI9b0Oe-U4,8970
|
||||||
|
yaml/scanner.py,sha256=KeQIKGNlSyPE8QDwionHxy9CgbqE5teJEz05FR9-nAg,51277
|
||||||
|
yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165
|
||||||
|
yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573
|
@ -0,0 +1,5 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: bdist_wheel (0.33.6)
|
||||||
|
Root-Is-Purelib: false
|
||||||
|
Tag: cp37-cp37m-win32
|
||||||
|
|
Binary file not shown.
@ -0,0 +1,91 @@
|
|||||||
|
Documentation: http://chrissimpkins.github.io/crypto/
|
||||||
|
|
||||||
|
Description
|
||||||
|
-------------
|
||||||
|
|
||||||
|
crypto provides a simple interface to symmetric Gnu Privacy Guard (gpg) encryption and decryption for one or more files on Unix and Linux platforms. It runs on top of gpg and requires a gpg install on your system. Encryption is performed with the AES256 cipher algorithm. `Benchmarks relative to default gpg settings are available for text and binary file mime types <https://chrissimpkins.github.io/crypto/benchmarks.html>`_.
|
||||||
|
|
||||||
|
crypto provides a number of options including automated tar archives of multiple files prior to encryption, portable ASCII armored encryption formatting, and SHA256 hash digest generation for your encrypted files. You can view all available options in the `usage documentation <http://chrissimpkins.github.io/crypto/usage.html>`_ or with the ``--help`` option.
|
||||||
|
|
||||||
|
Tested in cPython 2.7.x, 3.4.x, and pypy 2.4.x (Python version 2.7.9)
|
||||||
|
|
||||||
|
|
||||||
|
Install
|
||||||
|
---------
|
||||||
|
|
||||||
|
Install with ``pip`` using the command:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pip install crypto
|
||||||
|
|
||||||
|
or `download the source repository <https://github.com/chrissimpkins/crypto/tarball/master>`_, unpack it, and navigate to the top level of the repository. Then enter:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ python setup.py install
|
||||||
|
|
||||||
|
|
||||||
|
Upgrade
|
||||||
|
-----------
|
||||||
|
|
||||||
|
You can upgrade your crypto version with the command:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pip install --upgrade crypto
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
---------
|
||||||
|
|
||||||
|
Encryption (crypto)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ crypto <options> [file path] <file path 2...>
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ crypto <options> [directory path] <directory path 2...>
|
||||||
|
|
||||||
|
|
||||||
|
Decryption (decrypto)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ decrypto <options> [file path] <file path 2...>
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ decrypto <options> [directory path] <directory path 2...>
|
||||||
|
|
||||||
|
|
||||||
|
You can find all available options in the `documentation <http://chrissimpkins.github.io/crypto/usage.html>`_ or by using one of the following commands:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ crypto --help
|
||||||
|
$ decrypto --help
|
||||||
|
|
||||||
|
|
||||||
|
Frequently Asked Questions
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
`FAQ link <http://chrissimpkins.github.io/crypto/faq.html>`_
|
||||||
|
|
||||||
|
|
||||||
|
Issue Reporting
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Issue reporting is available on the `GitHub repository <https://github.com/chrissimpkins/crypto/issues>`_
|
||||||
|
|
||||||
|
|
||||||
|
Changelog
|
||||||
|
------------
|
||||||
|
|
||||||
|
`Changelog link <http://chrissimpkins.github.io/crypto/changelog.html>`_
|
||||||
|
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
pip
|
@ -0,0 +1,116 @@
|
|||||||
|
Metadata-Version: 2.0
|
||||||
|
Name: crypto
|
||||||
|
Version: 1.4.1
|
||||||
|
Summary: Simple symmetric GPG file encryption and decryption
|
||||||
|
Home-page: https://github.com/chrissimpkins/crypto
|
||||||
|
Author: Christopher Simpkins
|
||||||
|
Author-email: git.simpkins@gmail.com
|
||||||
|
License: MIT license
|
||||||
|
Keywords: encryption,decryption,gpg,pgp,openpgp,cipher,AES256,crypto,cryptography,security,privacy
|
||||||
|
Platform: any
|
||||||
|
Classifier: Intended Audience :: End Users/Desktop
|
||||||
|
Classifier: Topic :: Security :: Cryptography
|
||||||
|
Classifier: Topic :: Security
|
||||||
|
Classifier: Development Status :: 5 - Production/Stable
|
||||||
|
Classifier: Natural Language :: English
|
||||||
|
Classifier: License :: OSI Approved :: MIT License
|
||||||
|
Classifier: Operating System :: MacOS :: MacOS X
|
||||||
|
Classifier: Operating System :: POSIX
|
||||||
|
Classifier: Operating System :: Unix
|
||||||
|
Classifier: Programming Language :: Python
|
||||||
|
Classifier: Programming Language :: Python :: 2
|
||||||
|
Classifier: Programming Language :: Python :: 3
|
||||||
|
Requires-Dist: Naked
|
||||||
|
Requires-Dist: shellescape
|
||||||
|
|
||||||
|
Documentation: http://chrissimpkins.github.io/crypto/
|
||||||
|
|
||||||
|
Description
|
||||||
|
-------------
|
||||||
|
|
||||||
|
crypto provides a simple interface to symmetric Gnu Privacy Guard (gpg) encryption and decryption for one or more files on Unix and Linux platforms. It runs on top of gpg and requires a gpg install on your system. Encryption is performed with the AES256 cipher algorithm. `Benchmarks relative to default gpg settings are available for text and binary file mime types <https://chrissimpkins.github.io/crypto/benchmarks.html>`_.
|
||||||
|
|
||||||
|
crypto provides a number of options including automated tar archives of multiple files prior to encryption, portable ASCII armored encryption formatting, and SHA256 hash digest generation for your encrypted files. You can view all available options in the `usage documentation <http://chrissimpkins.github.io/crypto/usage.html>`_ or with the ``--help`` option.
|
||||||
|
|
||||||
|
Tested in cPython 2.7.x, 3.4.x, and pypy 2.4.x (Python version 2.7.9)
|
||||||
|
|
||||||
|
|
||||||
|
Install
|
||||||
|
---------
|
||||||
|
|
||||||
|
Install with ``pip`` using the command:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pip install crypto
|
||||||
|
|
||||||
|
or `download the source repository <https://github.com/chrissimpkins/crypto/tarball/master>`_, unpack it, and navigate to the top level of the repository. Then enter:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ python setup.py install
|
||||||
|
|
||||||
|
|
||||||
|
Upgrade
|
||||||
|
-----------
|
||||||
|
|
||||||
|
You can upgrade your crypto version with the command:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pip install --upgrade crypto
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
---------
|
||||||
|
|
||||||
|
Encryption (crypto)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ crypto <options> [file path] <file path 2...>
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ crypto <options> [directory path] <directory path 2...>
|
||||||
|
|
||||||
|
|
||||||
|
Decryption (decrypto)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ decrypto <options> [file path] <file path 2...>
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ decrypto <options> [directory path] <directory path 2...>
|
||||||
|
|
||||||
|
|
||||||
|
You can find all available options in the `documentation <http://chrissimpkins.github.io/crypto/usage.html>`_ or by using one of the following commands:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ crypto --help
|
||||||
|
$ decrypto --help
|
||||||
|
|
||||||
|
|
||||||
|
Frequently Asked Questions
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
`FAQ link <http://chrissimpkins.github.io/crypto/faq.html>`_
|
||||||
|
|
||||||
|
|
||||||
|
Issue Reporting
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Issue reporting is available on the `GitHub repository <https://github.com/chrissimpkins/crypto/issues>`_
|
||||||
|
|
||||||
|
|
||||||
|
Changelog
|
||||||
|
------------
|
||||||
|
|
||||||
|
`Changelog link <http://chrissimpkins.github.io/crypto/changelog.html>`_
|
||||||
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
|||||||
|
../../Scripts/crypto.exe,sha256=p3AgdHe-v7ncFcENaBUjHJ10ndwkAZd6dnzRmZKYpus,93000
|
||||||
|
../../Scripts/decrypto.exe,sha256=MYU2xn0LykZbbYskB8hTEIMqIKPNE9TTNDS3wkW1SbI,93008
|
||||||
|
crypto-1.4.1.data/scripts/crypto,sha256=JiOIH9vUcNmsPUqHi3sD7wa-KshHSSHsWwYxdJxquFU,287
|
||||||
|
crypto-1.4.1.data/scripts/decrypto,sha256=GFVx1pyc7wga_NuKaO5IN4VpiC-iB1nXFIzvwRKs-QI,291
|
||||||
|
crypto-1.4.1.dist-info/DESCRIPTION.rst,sha256=cUnYN7Bc2SHYRPCbrgoiwqxsilKu-F870exbHPzYyqg,2406
|
||||||
|
crypto-1.4.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||||
|
crypto-1.4.1.dist-info/METADATA,sha256=kQ8T3QfQ00YZ0ETfqAgpzT7JjQZyAtcbpdD4eO-LyfY,3354
|
||||||
|
crypto-1.4.1.dist-info/RECORD,,
|
||||||
|
crypto-1.4.1.dist-info/WHEEL,sha256=SXYYsi-y-rEGIva8sB8iKF6bAFD6YDhmqHX5hI3fc0o,110
|
||||||
|
crypto-1.4.1.dist-info/entry_points.txt,sha256=c4pXwtenCFUlTF9DHcgCWqXZChpJwNtbSjtfQMhrm3M,79
|
||||||
|
crypto-1.4.1.dist-info/pydist.json,sha256=Q2O_1hp0_7IBZ-JTH9MYE5-GNhTxeT1KqjRvp6U8YAs,1261
|
||||||
|
crypto-1.4.1.dist-info/top_level.txt,sha256=iQrOUgmGXiXK01vJOH4JP-6x-yip7JIM4OEshevZ9wE,7
|
||||||
|
crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
crypto/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
crypto/__pycache__/app.cpython-37.pyc,,
|
||||||
|
crypto/__pycache__/decryptoapp.cpython-37.pyc,,
|
||||||
|
crypto/__pycache__/settings.cpython-37.pyc,,
|
||||||
|
crypto/app.py,sha256=m9usVyS7G-vg7YYx7rVV1DWohss5xlKKq5vV-viSi8o,13952
|
||||||
|
crypto/decryptoapp.py,sha256=5hJEh55vyHSgMUkIp2PdYn5P1Z6qkFdrAnsu8EhJZ8c,22448
|
||||||
|
crypto/library/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||||
|
crypto/library/__pycache__/__init__.cpython-37.pyc,,
|
||||||
|
crypto/library/__pycache__/cryptor.cpython-37.pyc,,
|
||||||
|
crypto/library/__pycache__/hash.cpython-37.pyc,,
|
||||||
|
crypto/library/__pycache__/package.cpython-37.pyc,,
|
||||||
|
crypto/library/cryptor.py,sha256=0A6KMRsRo7UpfFw0qTbaVByqIRqO4bGSbckgR9fH-RQ,8934
|
||||||
|
crypto/library/hash.py,sha256=HVO5NcCRVHJRYOIAMTHlxzk1wWAeYuon2QwiRHJJaAY,847
|
||||||
|
crypto/library/package.py,sha256=mTXHpmJFRKqx_KGRN1bUfSei8duBARZKtH4hBQzXAi4,1754
|
||||||
|
crypto/settings.py,sha256=d--yWR6fCw10JO3tyQhCY1z36PzPsW7ElRmcikNd8vg,4506
|
@ -0,0 +1,6 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: bdist_wheel (0.22.0)
|
||||||
|
Root-Is-Purelib: true
|
||||||
|
Tag: py2-none-any
|
||||||
|
Tag: py3-none-any
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
[console_scripts]
|
||||||
|
crypto = crypto.app:main
|
||||||
|
decrypto = crypto.decryptoapp:main
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
{"license": "MIT license", "exports": {"console_scripts": {"crypto": "crypto.app:main", "decrypto": "crypto.decryptoapp:main"}}, "document_names": {"description": "DESCRIPTION.rst"}, "name": "crypto", "metadata_version": "2.0", "contacts": [{"role": "author", "email": "git.simpkins@gmail.com", "name": "Christopher Simpkins"}], "generator": "bdist_wheel (0.22.0)", "commands": {"wrap_console": {"crypto": "crypto.app:main", "decrypto": "crypto.decryptoapp:main"}}, "summary": "Simple symmetric GPG file encryption and decryption", "project_urls": {"Home": "https://github.com/chrissimpkins/crypto"}, "platform": "any", "run_requires": [{"requires": ["Naked", "shellescape"]}], "version": "1.4.1", "keywords": "encryption,decryption,gpg,pgp,openpgp,cipher,AES256,crypto,cryptography,security,privacy", "classifiers": ["Intended Audience :: End Users/Desktop", "Topic :: Security :: Cryptography", "Topic :: Security", "Development Status :: 5 - Production/Stable", "Natural Language :: English", "License :: OSI Approved :: MIT License", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX", "Operating System :: Unix", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3"], "extras": []}
|
@ -0,0 +1 @@
|
|||||||
|
crypto
|
@ -0,0 +1,262 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# crypto
|
||||||
|
# Copyright 2015 Christopher Simpkins
|
||||||
|
# MIT license
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Application start
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import sys
|
||||||
|
import getpass
|
||||||
|
from Naked.commandline import Command
|
||||||
|
from Naked.toolshed.system import dir_exists, file_exists, list_all_files, make_path, stderr
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
# [ Instantiate command line object ]
|
||||||
|
# used for all subsequent conditional logic in the CLI application
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
c = Command(sys.argv[0], sys.argv[1:])
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
# [ VALIDATION LOGIC ] - early validation of appropriate command syntax
|
||||||
|
# Test that user entered at least one argument to the executable, print usage if not
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
if not c.command_suite_validates():
|
||||||
|
from crypto.settings import usage as crypto_usage
|
||||||
|
print(crypto_usage)
|
||||||
|
sys.exit(1)
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
# [ HELP, VERSION, USAGE LOGIC ]
|
||||||
|
# Naked framework provides default help, usage, and version commands for all applications
|
||||||
|
# --> settings for user messages are assigned in the lib/crypto/settings.py file
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
if c.help(): # User requested crypto help information
|
||||||
|
from crypto.settings import help as crypto_help
|
||||||
|
print(crypto_help)
|
||||||
|
sys.exit(0)
|
||||||
|
elif c.usage(): # User requested crypto usage information
|
||||||
|
from crypto.settings import usage as crypto_usage
|
||||||
|
print(crypto_usage)
|
||||||
|
sys.exit(0)
|
||||||
|
elif c.version(): # User requested crypto version information
|
||||||
|
from crypto.settings import app_name, major_version, minor_version, patch_version
|
||||||
|
version_display_string = app_name + ' ' + major_version + '.' + minor_version + '.' + patch_version
|
||||||
|
print(version_display_string)
|
||||||
|
sys.exit(0)
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
# [ APPLICATION LOGIC ]
|
||||||
|
#
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
elif c.argc > 1:
|
||||||
|
# code for multi-file processing and commands that include options
|
||||||
|
# ASCII ARMOR SWITCH
|
||||||
|
ascii_armored = False
|
||||||
|
if c.option('--armor') or c.option('-a'):
|
||||||
|
ascii_armored = True
|
||||||
|
|
||||||
|
# MAX COMPRESS / COMPRESS ALL SWITCH
|
||||||
|
max_compress = False
|
||||||
|
if c.option('--space'):
|
||||||
|
max_compress = True
|
||||||
|
|
||||||
|
# NO COMPRESSION SWITCH
|
||||||
|
no_compress = False
|
||||||
|
if c.option('--speed'):
|
||||||
|
no_compress = True
|
||||||
|
|
||||||
|
# SECURE HASH DIGEST REPORT SWITCH
|
||||||
|
report_checksum = False
|
||||||
|
if c.option('--hash'):
|
||||||
|
report_checksum = True
|
||||||
|
|
||||||
|
# TAR FOLDERS SWITCH
|
||||||
|
tar_folders = False
|
||||||
|
if c.option('--tar'):
|
||||||
|
tar_folders = True
|
||||||
|
|
||||||
|
directory_list = [] # directory paths included in the user entered paths from the command line
|
||||||
|
tar_directory_list = [] # directories, which need to be packaged as tar archives
|
||||||
|
file_list = [] # file paths included in the user entered paths from the command line (and inside directories entered)
|
||||||
|
|
||||||
|
# dot and .crypt file flags for exclusion testing
|
||||||
|
contained_dot_file = False
|
||||||
|
contained_crypt_file = False
|
||||||
|
|
||||||
|
# determine if argument is an existing file or directory
|
||||||
|
for argument in c.argv:
|
||||||
|
if file_exists(argument):
|
||||||
|
if argument.endswith('.crypt'): # do not include previously encrypted files
|
||||||
|
contained_crypt_file = True
|
||||||
|
else:
|
||||||
|
file_list.append(argument) # add appropriate file paths to the file_list
|
||||||
|
elif dir_exists(argument):
|
||||||
|
directory_list.append(argument) # if it is a directory, add path to the directory_list
|
||||||
|
|
||||||
|
# add all file paths from user specified directories to the file_list
|
||||||
|
if len(directory_list) > 0:
|
||||||
|
if not tar_folders:
|
||||||
|
for directory in directory_list:
|
||||||
|
directory_file_list = list_all_files(directory)
|
||||||
|
for contained_file in directory_file_list:
|
||||||
|
if contained_file[0] == ".":
|
||||||
|
contained_dot_file = True # change the flag + is not included in file_list intentionally (no dot files)
|
||||||
|
elif contained_file.endswith('.crypt'):
|
||||||
|
contained_crypt_file = True # change the flag + is not included in file_list intentionally (no previously encrypted files)
|
||||||
|
else:
|
||||||
|
# otherwise add to the list for encryption
|
||||||
|
contained_file_path = make_path(directory, contained_file)
|
||||||
|
file_list.append(contained_file_path)
|
||||||
|
else:
|
||||||
|
# create (uncompressed) tar archive for every targeted folder and add the resulting archive to the file_list
|
||||||
|
# do not start tar file creation, yet (!) - it is more convenient for the user to first enter the passphrase then start processing
|
||||||
|
for directory in directory_list:
|
||||||
|
directory_file_path = directory + '.tar'
|
||||||
|
tar_directory_list.append(directory)
|
||||||
|
file_list.append(directory_file_path)
|
||||||
|
|
||||||
|
# confirm that there are files to be encrypted, if not warn user
|
||||||
|
if len(file_list) == 0:
|
||||||
|
if contained_dot_file is True or contained_crypt_file is True:
|
||||||
|
stderr("There were no files identified for encryption. crypto does not encrypt dot files or previously encrypted '.crypt' files.")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
stderr("Unable to identify files for encryption")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
# file_list should contain all filepaths from either user specified file paths or contained in top level of directory, encrypt them
|
||||||
|
passphrase = getpass.getpass("Please enter your passphrase: ")
|
||||||
|
if len(passphrase) == 0: # confirm that user entered a passphrase
|
||||||
|
stderr("You did not enter a passphrase. Please repeat your command and try again.")
|
||||||
|
sys.exit(1)
|
||||||
|
passphrase_confirm = getpass.getpass("Please enter your passphrase again: ")
|
||||||
|
|
||||||
|
if passphrase == passphrase_confirm:
|
||||||
|
|
||||||
|
# create temporary tar-files
|
||||||
|
tar_list = []
|
||||||
|
if len(tar_directory_list) > 0:
|
||||||
|
from crypto.library import package
|
||||||
|
tar_list = package.generate_tar_files(tar_directory_list)
|
||||||
|
for t in tar_list:
|
||||||
|
if t not in file_list: # check to confirm that the tar archive is in the list of files to encrypt
|
||||||
|
if file_exists(t):
|
||||||
|
# append the tarfile to the file_list for encryption if it was not included in the file list for encryption
|
||||||
|
file_list.append(t)
|
||||||
|
else:
|
||||||
|
stderr("There was an error with the tar archive creation. Please try again.", exit=1)
|
||||||
|
|
||||||
|
from crypto.library.cryptor import Cryptor
|
||||||
|
the_cryptor = Cryptor(passphrase)
|
||||||
|
|
||||||
|
# run encryption based upon any passed switches
|
||||||
|
if ascii_armored:
|
||||||
|
if max_compress:
|
||||||
|
the_cryptor.encrypt_files(file_list, force_nocompress=False, force_compress=True, armored=True, checksum=report_checksum)
|
||||||
|
elif no_compress:
|
||||||
|
the_cryptor.encrypt_files(file_list, force_nocompress=True, force_compress=False, armored=True, checksum=report_checksum)
|
||||||
|
else:
|
||||||
|
the_cryptor.encrypt_files(file_list, force_nocompress=False, force_compress=False, armored=True, checksum=report_checksum)
|
||||||
|
else:
|
||||||
|
if max_compress:
|
||||||
|
the_cryptor.encrypt_files(file_list, force_nocompress=False, force_compress=True, armored=False, checksum=report_checksum)
|
||||||
|
elif no_compress:
|
||||||
|
the_cryptor.encrypt_files(file_list, force_nocompress=True, force_compress=False, armored=False, checksum=report_checksum)
|
||||||
|
else:
|
||||||
|
the_cryptor.encrypt_files(file_list, force_nocompress=False, force_compress=False, armored=False, checksum=report_checksum)
|
||||||
|
|
||||||
|
# overwrite user entered passphrases
|
||||||
|
passphrase = ""
|
||||||
|
passphrase_confirm = ""
|
||||||
|
the_cryptor.cleanup()
|
||||||
|
|
||||||
|
# tmp tar file removal (generated with package.generate_tar_files function above)
|
||||||
|
if len(tar_list) > 0:
|
||||||
|
from crypto.library import package
|
||||||
|
package.remove_tar_files(tar_list)
|
||||||
|
else:
|
||||||
|
# passphrases did not match, report to user and abort
|
||||||
|
# overwrite user entered passphrases
|
||||||
|
passphrase = ""
|
||||||
|
passphrase_confirm = ""
|
||||||
|
stderr("The passphrases did not match. Please enter your command again.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
elif c.argc == 1:
|
||||||
|
# simple single file or directory processing with default settings
|
||||||
|
path = c.arg0
|
||||||
|
if file_exists(path):
|
||||||
|
# it is a file, encrypt the single file with default settings
|
||||||
|
# confirm that it is not already encrypted, abort if so
|
||||||
|
if path.endswith('.crypt'):
|
||||||
|
stderr("You are attempting to encrypt an encrypted file. Please delete the .crypt file and repeat encryption with the original file if this is your intent.")
|
||||||
|
sys.exit(1)
|
||||||
|
# if passes test above, obtain passphrase from the user
|
||||||
|
passphrase = getpass.getpass("Please enter your passphrase: ")
|
||||||
|
if len(passphrase) == 0: # confirm that user entered a passphrase
|
||||||
|
stderr("You did not enter a passphrase. Please repeat your command and try again.")
|
||||||
|
sys.exit(1)
|
||||||
|
passphrase_confirm = getpass.getpass("Please enter your passphrase again: ")
|
||||||
|
|
||||||
|
if passphrase == passphrase_confirm:
|
||||||
|
from crypto.library.cryptor import Cryptor
|
||||||
|
the_cryptor = Cryptor(passphrase)
|
||||||
|
the_cryptor.encrypt_file(path)
|
||||||
|
the_cryptor.cleanup()
|
||||||
|
else:
|
||||||
|
stderr("The passphrases did not match. Please enter your command again.")
|
||||||
|
sys.exit(1)
|
||||||
|
elif dir_exists(path):
|
||||||
|
# it is a directory, encrypt all top level files with default settings
|
||||||
|
dirty_directory_file_list = list_all_files(path)
|
||||||
|
# remove dot files and previously encrypted files (with .crypt suffix) from the list of directory files
|
||||||
|
clean_directory_file_list = [x for x in dirty_directory_file_list if x[0] != "." and x.endswith(".crypt") is False] # remove dotfiles and .crypt files
|
||||||
|
|
||||||
|
# confirm that there are still files in the list after the dot files and encrypted files are removed
|
||||||
|
if len(clean_directory_file_list) == 0:
|
||||||
|
stderr("There are no unencrypted files in the directory.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# create relative file paths for each file in the clean_directory_file_list
|
||||||
|
clean_directory_file_list_relpaths = []
|
||||||
|
for clean_file in clean_directory_file_list:
|
||||||
|
new_file_path = make_path(path, clean_file)
|
||||||
|
clean_directory_file_list_relpaths.append(new_file_path)
|
||||||
|
|
||||||
|
# prompt for the passphrase
|
||||||
|
passphrase = getpass.getpass("Please enter your passphrase: ")
|
||||||
|
if len(passphrase) == 0: # confirm that user entered a passphrase
|
||||||
|
stderr("You did not enter a passphrase. Please repeat your command and try again.")
|
||||||
|
sys.exit(1)
|
||||||
|
passphrase_confirm = getpass.getpass("Please enter your passphrase again: ")
|
||||||
|
|
||||||
|
if passphrase == passphrase_confirm:
|
||||||
|
from crypto.library.cryptor import Cryptor
|
||||||
|
the_cryptor = Cryptor(passphrase)
|
||||||
|
the_cryptor.encrypt_files(clean_directory_file_list_relpaths) # encrypt the list of directory files
|
||||||
|
the_cryptor.cleanup()
|
||||||
|
else:
|
||||||
|
# passphrases do not match
|
||||||
|
# overwrite user entered passphrases
|
||||||
|
passphrase = ""
|
||||||
|
passphrase_confirm = ""
|
||||||
|
stderr("The passphrases did not match. Please enter your command again.")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
# error message, not a file or directory. user entry error
|
||||||
|
stderr("The path that you entered does not appear to be an existing file or directory. Please try again.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
# [ DEFAULT MESSAGE FOR MATCH FAILURE ]
|
||||||
|
# Message to provide to the user when all above conditional logic fails to meet a true condition
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
else:
|
||||||
|
print("Could not complete your request. Please try again.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -0,0 +1,380 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# decrypto
|
||||||
|
# Copyright 2015 Christopher Simpkins
|
||||||
|
# MIT license
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Application start
|
||||||
|
def main():
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from time import sleep
|
||||||
|
import getpass
|
||||||
|
import tarfile
|
||||||
|
from Naked.commandline import Command
|
||||||
|
from Naked.toolshed.shell import execute, muterun
|
||||||
|
from Naked.toolshed.system import dir_exists, file_exists, list_all_files, make_path, stdout, stderr, is_dir
|
||||||
|
from shellescape import quote
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
# [ Instantiate command line object ]
|
||||||
|
# used for all subsequent conditional logic in the CLI application
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
c = Command(sys.argv[0], sys.argv[1:])
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
# [ VALIDATION LOGIC ] - early validation of appropriate command syntax
|
||||||
|
# Test that user entered at least one argument to the executable, print usage if not
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
if not c.command_suite_validates():
|
||||||
|
from crypto.settings import usage as crypto_usage
|
||||||
|
print(crypto_usage)
|
||||||
|
sys.exit(1)
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
# [ HELP, VERSION, USAGE LOGIC ]
|
||||||
|
# Naked framework provides default help, usage, and version commands for all applications
|
||||||
|
# --> settings for user messages are assigned in the lib/crypto/settings.py file
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
if c.help(): # User requested crypto help information
|
||||||
|
from crypto.settings import help as crypto_help
|
||||||
|
print(crypto_help)
|
||||||
|
sys.exit(0)
|
||||||
|
elif c.usage(): # User requested crypto usage information
|
||||||
|
from crypto.settings import usage as crypto_usage
|
||||||
|
print(crypto_usage)
|
||||||
|
sys.exit(0)
|
||||||
|
elif c.version(): # User requested crypto version information
|
||||||
|
from crypto.settings import app_name, major_version, minor_version, patch_version
|
||||||
|
version_display_string = app_name + ' ' + major_version + '.' + minor_version + '.' + patch_version
|
||||||
|
print(version_display_string)
|
||||||
|
sys.exit(0)
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
# [ APPLICATION LOGIC ]
|
||||||
|
#
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
elif c.argc > 1:
|
||||||
|
# code for multi-file processing and commands that include options
|
||||||
|
use_standard_output = False # print to stdout flag
|
||||||
|
use_file_overwrite = False # overwrite existing file
|
||||||
|
untar_archives = True # untar decrypted tar archives, true by default
|
||||||
|
|
||||||
|
# set user option flags
|
||||||
|
if c.option('--stdout') or c.option('-s'):
|
||||||
|
use_standard_output = True
|
||||||
|
if c.option('--overwrite') or c.option('-o'):
|
||||||
|
use_file_overwrite = True
|
||||||
|
if c.option('--nountar'):
|
||||||
|
untar_archives = False
|
||||||
|
|
||||||
|
directory_list = [] # directory paths included in the user entered paths from the command line
|
||||||
|
file_list = [] # file paths included in the user entered paths from the command line (and inside directories entered)
|
||||||
|
|
||||||
|
for argument in c.argv:
|
||||||
|
if file_exists(argument): # user included a file, add it to the file_list for decryption
|
||||||
|
if argument.endswith('.crypt'):
|
||||||
|
file_list.append(argument) # add .crypt files to the list of files for decryption
|
||||||
|
elif argument.endswith('.gpg'):
|
||||||
|
file_list.append(argument)
|
||||||
|
elif argument.endswith('.asc'):
|
||||||
|
file_list.append(argument)
|
||||||
|
elif argument.endswith('.pgp'):
|
||||||
|
file_list.append(argument)
|
||||||
|
else:
|
||||||
|
# cannot identify as an encrypted file, give it a shot anyways but warn user
|
||||||
|
file_list.append(argument)
|
||||||
|
stdout("Could not confirm that '" + argument + "' is encrypted based upon the file type. Attempting decryption. Keep your fingers crossed...")
|
||||||
|
elif dir_exists(argument): # user included a directory, add it to the directory_list
|
||||||
|
directory_list.append(argument)
|
||||||
|
else:
|
||||||
|
if argument[0] == "-":
|
||||||
|
pass # if it is an option, do nothing
|
||||||
|
else:
|
||||||
|
stderr("'" + argument + "' does not appear to be an existing file or directory. Aborting decryption attempt for this request.")
|
||||||
|
|
||||||
|
# unroll the contained directory files into the file_list IF they are encrypted file types
|
||||||
|
if len(directory_list) > 0:
|
||||||
|
for directory in directory_list:
|
||||||
|
directory_file_list = list_all_files(directory)
|
||||||
|
for contained_file in directory_file_list:
|
||||||
|
if contained_file.endswith('.crypt'):
|
||||||
|
file_list.append(make_path(directory, contained_file)) # include the file with a filepath 'directory path/contained_file path'
|
||||||
|
elif contained_file.endswith('.gpg'):
|
||||||
|
file_list.append(make_path(directory, contained_file))
|
||||||
|
elif contained_file.endswith('asc'):
|
||||||
|
file_list.append(make_path(directory, contained_file))
|
||||||
|
elif contained_file.endswith('.pgp'):
|
||||||
|
file_list.append(make_path(directory, contained_file))
|
||||||
|
|
||||||
|
# confirm that there are files for decryption, if not abort
|
||||||
|
if len(file_list) == 0:
|
||||||
|
stderr("Could not identify files for decryption")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# get passphrase used to symmetrically decrypt the file
|
||||||
|
passphrase = getpass.getpass("Please enter your passphrase: ")
|
||||||
|
if len(passphrase) == 0: # confirm that user entered a passphrase
|
||||||
|
stderr("You did not enter a passphrase. Please repeat your command and try again.")
|
||||||
|
sys.exit(1)
|
||||||
|
passphrase_confirm = getpass.getpass("Please enter your passphrase again: ")
|
||||||
|
|
||||||
|
if passphrase == passphrase_confirm:
|
||||||
|
# begin decryption of each requested file. the directory path was already added to the file path above
|
||||||
|
for encrypted_file in file_list:
|
||||||
|
# create the decrypted file name
|
||||||
|
decrypted_filename = ""
|
||||||
|
if encrypted_file.endswith('.crypt'):
|
||||||
|
decrypted_filename = encrypted_file[0:-6]
|
||||||
|
elif encrypted_file.endswith('.gpg') or encrypted_file.endswith('.asc') or encrypted_file.endswith('.pgp'):
|
||||||
|
decrypted_filename = encrypted_file[0:-4]
|
||||||
|
else:
|
||||||
|
decrypted_filename = encrypted_file + '.decrypt' # if it was a file without a known encrypted file type, add the .decrypt suffix
|
||||||
|
|
||||||
|
# determine whether file overwrite will take place with the decrypted file
|
||||||
|
skip_file = False # flag that indicates this file should not be encrypted
|
||||||
|
created_tmp_files = False
|
||||||
|
if not use_standard_output: # if not writing a file, no need to check for overwrite
|
||||||
|
if file_exists(decrypted_filename):
|
||||||
|
if use_file_overwrite: # rename the existing file to temp file which will be erased or replaced (on decryption failures) below
|
||||||
|
tmp_filename = decrypted_filename + '.tmp'
|
||||||
|
os.rename(decrypted_filename, tmp_filename)
|
||||||
|
created_tmp_files = True
|
||||||
|
else:
|
||||||
|
stdout("The file path '" + decrypted_filename + "' already exists. This file was not decrypted.")
|
||||||
|
skip_file = True
|
||||||
|
|
||||||
|
# begin decryption
|
||||||
|
if not skip_file:
|
||||||
|
if use_standard_output: # using --quiet flag to suppress stdout messages from gpg, just want the file data in stdout stream
|
||||||
|
system_command = "gpg --batch --quiet --passphrase " + quote(passphrase) + " -d " + quote(encrypted_file)
|
||||||
|
successful_execution = execute(system_command) # use naked execute function to directly push to stdout, rather than return stdout
|
||||||
|
|
||||||
|
if not successful_execution:
|
||||||
|
stderr("Unable to decrypt file '" + encrypted_file + "'", 0)
|
||||||
|
if created_tmp_files: # restore the moved tmp file to original if decrypt failed
|
||||||
|
tmp_filename = decrypted_filename + '.tmp'
|
||||||
|
if file_exists(tmp_filename):
|
||||||
|
os.rename(tmp_filename, decrypted_filename)
|
||||||
|
else: # decryption successful but we are in stdout flag so do not include any other output from decrypto
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
system_command = "gpg --batch -o " + quote(decrypted_filename) + " --passphrase " + quote(passphrase) + " -d " + quote(encrypted_file)
|
||||||
|
response = muterun(system_command)
|
||||||
|
|
||||||
|
if response.exitcode == 0:
|
||||||
|
stdout("'" + encrypted_file + "' decrypted to '" + decrypted_filename + "'")
|
||||||
|
else: # failed decryption
|
||||||
|
if created_tmp_files: # restore the moved tmp file to original if decrypt failed
|
||||||
|
tmp_filename = decrypted_filename + '.tmp'
|
||||||
|
if file_exists(tmp_filename):
|
||||||
|
os.rename(tmp_filename, decrypted_filename)
|
||||||
|
# report the error
|
||||||
|
stderr(response.stderr)
|
||||||
|
stderr("Decryption failed for " + encrypted_file)
|
||||||
|
|
||||||
|
# cleanup: remove the tmp file
|
||||||
|
if created_tmp_files:
|
||||||
|
tmp_filename = decrypted_filename + '.tmp'
|
||||||
|
if file_exists(tmp_filename):
|
||||||
|
os.remove(tmp_filename)
|
||||||
|
|
||||||
|
# untar/extract any detected archive file(s)
|
||||||
|
if untar_archives is True:
|
||||||
|
if decrypted_filename.endswith('.tar') and tarfile.is_tarfile(decrypted_filename):
|
||||||
|
untar_path_tuple = os.path.split(decrypted_filename)
|
||||||
|
untar_path = untar_path_tuple[0]
|
||||||
|
if use_file_overwrite:
|
||||||
|
with tarfile.open(decrypted_filename) as tar:
|
||||||
|
if len(untar_path) > 0:
|
||||||
|
tar.extractall(path=untar_path) # use dir path from the decrypted_filename if not CWD
|
||||||
|
stdout("'" + decrypted_filename + "' unpacked in the directory path '" + untar_path + "'")
|
||||||
|
else:
|
||||||
|
tar.extractall() # else use CWD
|
||||||
|
stdout("'" + decrypted_filename + "' unpacked in the current working directory")
|
||||||
|
else:
|
||||||
|
with tarfile.TarFile(decrypted_filename, 'r', errorlevel=1) as tar:
|
||||||
|
for tarinfo in tar:
|
||||||
|
t_file = tarinfo.name
|
||||||
|
if len(untar_path) > 0:
|
||||||
|
t_file_path = os.path.join(untar_path, t_file)
|
||||||
|
else:
|
||||||
|
t_file_path = t_file
|
||||||
|
if not os.path.exists(t_file_path):
|
||||||
|
try:
|
||||||
|
if len(untar_path) > 0:
|
||||||
|
tar.extract(t_file, path=untar_path) # write to the appropriate dir
|
||||||
|
else:
|
||||||
|
tar.extract(t_file) # write to CWD
|
||||||
|
except IOError as e:
|
||||||
|
stderr(
|
||||||
|
"Failed to unpack the file '" + t_file_path + "' [" + str(
|
||||||
|
e) + "]")
|
||||||
|
elif is_dir(t_file_path):
|
||||||
|
pass # do nothing if it exists and is a directory, no need to warn
|
||||||
|
else: # it is a file and it already exists, provide user error message
|
||||||
|
stderr(
|
||||||
|
"Failed to unpack the file '" + t_file_path + "'. File already exists. Use the --overwrite flag to replace existing files.")
|
||||||
|
|
||||||
|
# remove the decrypted tar archive file
|
||||||
|
os.remove(decrypted_filename)
|
||||||
|
|
||||||
|
# overwrite the entered passphrases after file decryption is complete for all files
|
||||||
|
passphrase = ""
|
||||||
|
passphrase_confirm = ""
|
||||||
|
|
||||||
|
# add a short pause to hinder brute force pexpect style password attacks with decrypto
|
||||||
|
sleep(0.2) # 200ms pause
|
||||||
|
|
||||||
|
else: # passphrases did not match
|
||||||
|
passphrase = ""
|
||||||
|
passphrase_confirm = ""
|
||||||
|
stderr("The passphrases did not match. Please enter your command again.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
elif c.argc == 1:
|
||||||
|
# simple single file or directory processing with default settings
|
||||||
|
path = c.arg0
|
||||||
|
if file_exists(path): # SINGLE FILE
|
||||||
|
check_existing_file = False # check for a file with the name of new decrypted filename in the directory
|
||||||
|
|
||||||
|
if path.endswith('.crypt'):
|
||||||
|
decrypted_filename = path[0:-6] # remove the .crypt suffix
|
||||||
|
check_existing_file = True
|
||||||
|
elif path.endswith('.gpg') or path.endswith('.pgp') or path.endswith('.asc'):
|
||||||
|
decrypted_filename = path[0:-4]
|
||||||
|
check_existing_file = True
|
||||||
|
else:
|
||||||
|
decrypted_filename = path + ".decrypt" # if there is not a standard file type, then add a .decrypt suffix to the decrypted file name
|
||||||
|
stdout("Could not confirm that the requested file is encrypted based upon the file type. Attempting decryption. Keep your fingers crossed...")
|
||||||
|
|
||||||
|
# confirm that the decrypted path does not already exist, if so abort with warning message to user
|
||||||
|
if check_existing_file is True:
|
||||||
|
if file_exists(decrypted_filename):
|
||||||
|
stderr("Your file will be decrypted to '" + decrypted_filename + "' and this file path already exists. Please move the file or use the --overwrite option with your command if you intend to replace the current file.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# get passphrase used to symmetrically decrypt the file
|
||||||
|
passphrase = getpass.getpass("Please enter your passphrase: ")
|
||||||
|
if len(passphrase) == 0: # confirm that user entered a passphrase
|
||||||
|
stderr("You did not enter a passphrase. Please repeat your command and try again.")
|
||||||
|
sys.exit(1)
|
||||||
|
passphrase_confirm = getpass.getpass("Please enter your passphrase again: ")
|
||||||
|
|
||||||
|
# confirm that the passphrases match
|
||||||
|
if passphrase == passphrase_confirm:
|
||||||
|
system_command = "gpg --batch -o " + quote(decrypted_filename) + " --passphrase " + quote(passphrase) + " -d " + quote(path)
|
||||||
|
response = muterun(system_command)
|
||||||
|
|
||||||
|
if response.exitcode == 0:
|
||||||
|
# unpack tar archive generated from the decryption, if present
|
||||||
|
if decrypted_filename.endswith('.tar') and tarfile.is_tarfile(decrypted_filename):
|
||||||
|
untar_path_tuple = os.path.split(decrypted_filename)
|
||||||
|
untar_path = untar_path_tuple[0]
|
||||||
|
|
||||||
|
with tarfile.TarFile(decrypted_filename, 'r', errorlevel=1) as tar:
|
||||||
|
for tarinfo in tar:
|
||||||
|
t_file = tarinfo.name
|
||||||
|
if len(untar_path) > 0:
|
||||||
|
t_file_path = os.path.join(untar_path, t_file)
|
||||||
|
else:
|
||||||
|
t_file_path = t_file
|
||||||
|
if not os.path.exists(t_file_path):
|
||||||
|
try:
|
||||||
|
if len(untar_path) > 0:
|
||||||
|
tar.extract(t_file, path=untar_path) # write to the appropriate dir
|
||||||
|
else:
|
||||||
|
tar.extract(t_file) # write to CWD
|
||||||
|
except IOError as e:
|
||||||
|
stderr("Failed to unpack the file '" + t_file_path + "' [" + str(e) + "]")
|
||||||
|
elif is_dir(t_file_path):
|
||||||
|
pass # do nothing if it exists and is a directory, no need to warn
|
||||||
|
else: # it is a file and it already exists, provide user error message
|
||||||
|
stderr("Failed to unpack the file '" + t_file_path + "'. File already exists. Use the --overwrite flag to replace existing files.")
|
||||||
|
|
||||||
|
# remove the decrypted tar archive
|
||||||
|
os.remove(decrypted_filename)
|
||||||
|
|
||||||
|
stdout("Decryption complete")
|
||||||
|
# overwrite user entered passphrases
|
||||||
|
passphrase = ""
|
||||||
|
passphrase_confirm = ""
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
stderr(response.stderr)
|
||||||
|
stderr("Decryption failed")
|
||||||
|
# overwrite user entered passphrases
|
||||||
|
passphrase = ""
|
||||||
|
passphrase_confirm = ""
|
||||||
|
# add a short pause to hinder brute force pexpect style password attacks with decrypto
|
||||||
|
sleep(0.2) # 200ms pause
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
stderr("The passphrases did not match. Please enter your command again.")
|
||||||
|
sys.exit(1)
|
||||||
|
elif dir_exists(path): # SINGLE DIRECTORY
|
||||||
|
dirty_directory_file_list = list_all_files(path)
|
||||||
|
directory_file_list = [x for x in dirty_directory_file_list if (x.endswith('.crypt') or x.endswith('.gpg') or x.endswith('.pgp') or x.endswith('.asc'))]
|
||||||
|
|
||||||
|
# if there are no encrypted files found, warn and abort
|
||||||
|
if len(directory_file_list) == 0:
|
||||||
|
stderr("There are no encrypted files in the directory")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# prompt for the passphrase
|
||||||
|
passphrase = getpass.getpass("Please enter your passphrase: ")
|
||||||
|
if len(passphrase) == 0: # confirm that user entered a passphrase
|
||||||
|
stderr("You did not enter a passphrase. Please repeat your command and try again.")
|
||||||
|
sys.exit(1)
|
||||||
|
passphrase_confirm = getpass.getpass("Please enter your passphrase again: ")
|
||||||
|
|
||||||
|
if passphrase == passphrase_confirm:
|
||||||
|
# decrypt all of the encypted files in the directory
|
||||||
|
for filepath in directory_file_list:
|
||||||
|
absolute_filepath = make_path(path, filepath) # combine the directory path and file name into absolute path
|
||||||
|
|
||||||
|
# remove file suffix from the decrypted file path that writes to disk
|
||||||
|
if absolute_filepath.endswith('.crypt'):
|
||||||
|
decrypted_filepath = absolute_filepath[0:-6] # remove the .crypt suffix
|
||||||
|
elif absolute_filepath.endswith('.gpg') or absolute_filepath.endswith('.pgp') or absolute_filepath.endswith('.asc'):
|
||||||
|
decrypted_filepath = absolute_filepath[0:-4]
|
||||||
|
|
||||||
|
# confirm that the file does not already exist
|
||||||
|
if file_exists(decrypted_filepath):
|
||||||
|
stdout("The file path '" + decrypted_filepath + "' already exists. This file was not decrypted.")
|
||||||
|
else:
|
||||||
|
system_command = "gpg --batch -o " + quote(decrypted_filepath) + " --passphrase " + quote(passphrase) + " -d " + quote(absolute_filepath)
|
||||||
|
response = muterun(system_command)
|
||||||
|
|
||||||
|
if response.exitcode == 0:
|
||||||
|
stdout("'" + absolute_filepath + "' decrypted to '" + decrypted_filepath + "'")
|
||||||
|
else:
|
||||||
|
stderr(response.stderr)
|
||||||
|
stderr("Decryption failed for " + absolute_filepath)
|
||||||
|
# overwrite user entered passphrases
|
||||||
|
passphrase = ""
|
||||||
|
passphrase_confirm = ""
|
||||||
|
|
||||||
|
# add a short pause to hinder brute force pexpect style password attacks with decrypto
|
||||||
|
sleep(0.2) # 200ms pause
|
||||||
|
else:
|
||||||
|
# overwrite user entered passphrases
|
||||||
|
passphrase = ""
|
||||||
|
passphrase_confirm = ""
|
||||||
|
stderr("The passphrases did not match. Please enter your command again.")
|
||||||
|
sys.exit(1)
|
||||||
|
else:
|
||||||
|
# error message, not a file or directory. user entry error
|
||||||
|
stderr("The path that you entered does not appear to be an existing file or directory. Please try again.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
# [ DEFAULT MESSAGE FOR MATCH FAILURE ]
|
||||||
|
# Message to provide to the user when all above conditional logic fails to meet a true condition
|
||||||
|
# ------------------------------------------------------------------------------------------
|
||||||
|
else:
|
||||||
|
print("Could not complete your request. Please try again.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
@ -0,0 +1,167 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from Naked.toolshed.shell import muterun
|
||||||
|
from Naked.toolshed.system import file_size, stdout, stderr
|
||||||
|
|
||||||
|
from shellescape import quote
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Cryptor class
|
||||||
|
# performs gpg encryption of one or more files
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class Cryptor(object):
|
||||||
|
"""performs gpg encryption of one or more files"""
|
||||||
|
def __init__(self, passphrase):
|
||||||
|
self.command_default = "gpg -z 1 --batch --force-mdc --cipher-algo AES256 -o "
|
||||||
|
self.command_nocompress = "gpg -z 0 --batch --force-mdc --cipher-algo AES256 -o "
|
||||||
|
self.command_maxcompress = "gpg -z 7 --batch --force-mdc --cipher-algo AES256 -o "
|
||||||
|
self.command_default_armored = "gpg -z 1 --armor --batch --force-mdc --cipher-algo AES256 -o "
|
||||||
|
self.command_nocompress_armored = "gpg -z 0 --armor --batch --force-mdc --cipher-algo AES256 -o "
|
||||||
|
self.command_maxcompress_armored = "gpg -z 7 --armor --batch --force-mdc --cipher-algo AES256 -o "
|
||||||
|
self.passphrase = passphrase
|
||||||
|
self.common_binaries = set(['.7z', '.gz', '.aac', '.app', '.avi', '.azw', '.bz2', '.deb', '.doc', '.dmg', '.exe', '.flv', '.gif', '.jar', '.jpg', '.mov', '.mp3', '.mp4', '.odt', '.oga', '.ogg', '.ogm', '.pdf', '.pkg', '.png', '.ppt', '.pps', '.psd', '.rar', '.rpm', '.tar', '.tif', '.wav', '.wma', '.wmv', '.xls', '.zip', '.aiff', '.docx', '.epub', '.flac', '.mpeg', '.jpeg', '.pptx', '.xlsx'])
|
||||||
|
self.common_text = set(['.c', '.h', '.m', '.cc', '.js', '.pl', '.py', '.rb', '.sh', '.cpp', '.css', '.csv', '.php', '.rss', '.txt', '.xml', '.yml', '.java', '.json', '.html', '.yaml'])
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# PUBLIC methods
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# encrypt_file : file encryption method
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
def encrypt_file(self, inpath, force_nocompress=False, force_compress=False, armored=False, checksum=False):
|
||||||
|
"""public method for single file encryption with optional compression, ASCII armored formatting, and file hash digest generation"""
|
||||||
|
if armored:
|
||||||
|
if force_compress:
|
||||||
|
command_stub = self.command_maxcompress_armored
|
||||||
|
elif force_nocompress:
|
||||||
|
command_stub = self.command_nocompress_armored
|
||||||
|
else:
|
||||||
|
if self._is_compress_filetype(inpath):
|
||||||
|
command_stub = self.command_default_armored
|
||||||
|
else:
|
||||||
|
command_stub = self.command_nocompress_armored
|
||||||
|
else:
|
||||||
|
if force_compress:
|
||||||
|
command_stub = self.command_maxcompress
|
||||||
|
elif force_nocompress:
|
||||||
|
command_stub = self.command_nocompress
|
||||||
|
else:
|
||||||
|
if self._is_compress_filetype(inpath):
|
||||||
|
command_stub = self.command_default
|
||||||
|
else:
|
||||||
|
command_stub = self.command_nocompress
|
||||||
|
|
||||||
|
encrypted_outpath = self._create_outfilepath(inpath)
|
||||||
|
system_command = command_stub + encrypted_outpath + " --passphrase " + quote(self.passphrase) + " --symmetric " + quote(inpath)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = muterun(system_command)
|
||||||
|
# check returned status code
|
||||||
|
if response.exitcode == 0:
|
||||||
|
stdout(encrypted_outpath + " was generated from " + inpath)
|
||||||
|
if checksum: # add a SHA256 hash digest of the encrypted file - requested by user --hash flag in command
|
||||||
|
from crypto.library import hash
|
||||||
|
encrypted_file_hash = hash.generate_hash(encrypted_outpath)
|
||||||
|
if len(encrypted_file_hash) == 64:
|
||||||
|
stdout("SHA256 hash digest for " + encrypted_outpath + " :")
|
||||||
|
stdout(encrypted_file_hash)
|
||||||
|
else:
|
||||||
|
stdout("Unable to generate a SHA256 hash digest for the file " + encrypted_outpath)
|
||||||
|
else:
|
||||||
|
stderr(response.stderr, 0)
|
||||||
|
stderr("Encryption failed")
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
stderr("There was a problem with the execution of gpg. Encryption failed. Error: [" + str(e) + "]")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# encrypt_files : multiple file encryption
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
def encrypt_files(self, file_list, force_nocompress=False, force_compress=False, armored=False, checksum=False):
|
||||||
|
"""public method for multiple file encryption with optional compression, ASCII armored formatting, and file hash digest generation"""
|
||||||
|
for the_file in file_list:
|
||||||
|
self.encrypt_file(the_file, force_nocompress, force_compress, armored, checksum)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# cleanup : overwrite the passphrase in memory
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
def cleanup(self):
|
||||||
|
"""public method that overwrites user passphrase in memory"""
|
||||||
|
self.passphrase = ""
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# PRIVATE methods
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _create_outfilepath(self, inpath):
|
||||||
|
"""private method that generates the crypto saved file path string with a .crypt file type"""
|
||||||
|
return inpath + '.crypt'
|
||||||
|
|
||||||
|
def _is_compress_filetype(self, inpath):
|
||||||
|
"""private method that performs magic number and size check on file to determine whether to compress the file"""
|
||||||
|
# check for common file type suffixes in order to avoid the need for file reads to check magic number for binary vs. text file
|
||||||
|
if self._is_common_binary(inpath):
|
||||||
|
return False
|
||||||
|
elif self._is_common_text(inpath):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# files > 10kB get checked for compression (arbitrary decision to skip compression on small files)
|
||||||
|
the_file_size = file_size(inpath)
|
||||||
|
if the_file_size > 10240:
|
||||||
|
if the_file_size > 512000: # seems to be a break point at ~ 500kb where file compression offset by additional file read, so limit tests to files > 500kB
|
||||||
|
try:
|
||||||
|
system_command = "file --mime-type -b " + quote(inpath)
|
||||||
|
response = muterun(system_command)
|
||||||
|
if response.stdout[0:5] == "text/": # check for a text file mime type
|
||||||
|
return True # appropriate size, appropriate file mime type
|
||||||
|
else:
|
||||||
|
return False # appropriate size, inappropriate file mime type
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True # if file size is < 500kB, skip the additional file read and just go with compression
|
||||||
|
else:
|
||||||
|
return False # below minimum size to consider compression, do not compress
|
||||||
|
|
||||||
|
def _is_common_binary(self, inpath):
|
||||||
|
"""private method to compare file path mime type to common binary file types"""
|
||||||
|
# make local variables for the available char numbers in the suffix types to be tested
|
||||||
|
two_suffix = inpath[-3:]
|
||||||
|
three_suffix = inpath[-4:]
|
||||||
|
four_suffix = inpath[-5:]
|
||||||
|
|
||||||
|
# test for inclusion in the instance variable common_binaries (defined in __init__)
|
||||||
|
if two_suffix in self.common_binaries:
|
||||||
|
return True
|
||||||
|
elif three_suffix in self.common_binaries:
|
||||||
|
return True
|
||||||
|
elif four_suffix in self.common_binaries:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _is_common_text(self, inpath):
|
||||||
|
"""private method to compare file path mime type to common text file types"""
|
||||||
|
# make local variables for the available char numbers in the suffix types to be tested
|
||||||
|
one_suffix = inpath[-2:]
|
||||||
|
two_suffix = inpath[-3:]
|
||||||
|
three_suffix = inpath[-4:]
|
||||||
|
four_suffix = inpath[-5:]
|
||||||
|
|
||||||
|
# test for inclusion in the instance variable common_text (defined in __init__)
|
||||||
|
if one_suffix in self.common_text:
|
||||||
|
return True
|
||||||
|
elif two_suffix in self.common_text:
|
||||||
|
return True
|
||||||
|
elif three_suffix in self.common_text:
|
||||||
|
return True
|
||||||
|
elif four_suffix in self.common_text:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
from Naked.toolshed.file import FileReader
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# PUBLIC
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
def generate_hash(filepath):
|
||||||
|
"""Public function that reads a local file and generates a SHA256 hash digest for it"""
|
||||||
|
fr = FileReader(filepath)
|
||||||
|
data = fr.read_bin()
|
||||||
|
return _calculate_sha256(data)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# PRIVATE
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
def _calculate_sha256(binary_string):
|
||||||
|
"""Private function that calculates a SHA256 hash digest for a binary string argument"""
|
||||||
|
return hashlib.sha256(binary_string).hexdigest()
|
||||||
|
|
@ -0,0 +1,45 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tarfile
|
||||||
|
from Naked.toolshed.system import stderr, dir_exists, file_exists
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# PUBLIC
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def generate_tar_files(directory_list):
|
||||||
|
"""Public function that reads a list of local directories and generates tar archives from them"""
|
||||||
|
|
||||||
|
tar_file_list = []
|
||||||
|
|
||||||
|
for directory in directory_list:
|
||||||
|
if dir_exists(directory):
|
||||||
|
_generate_tar(directory) # create the tar archive
|
||||||
|
tar_file_list.append(directory + '.tar') # append the tar archive filename to the returned tar_file_list list
|
||||||
|
else:
|
||||||
|
stderr("The directory '" + directory + "' does not exist and a tar archive could not be created from it.", exit=1)
|
||||||
|
|
||||||
|
return tar_file_list
|
||||||
|
|
||||||
|
|
||||||
|
def remove_tar_files(file_list):
|
||||||
|
"""Public function that removes temporary tar archive files in a local directory"""
|
||||||
|
for f in file_list:
|
||||||
|
if file_exists(f) and f.endswith('.tar'):
|
||||||
|
os.remove(f) # remove any tar files in the list, if it does not appear to be a tar file, leave it alone
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# PRIVATE
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_tar(dir_path):
|
||||||
|
"""Private function that reads a local directory and generates a tar archive from it"""
|
||||||
|
try:
|
||||||
|
with tarfile.open(dir_path + '.tar', 'w') as tar:
|
||||||
|
tar.add(dir_path)
|
||||||
|
except tarfile.TarError as e:
|
||||||
|
stderr("Error: tar archive creation failed [" + str(e) + "]", exit=1)
|
@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Application Name
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
app_name = 'crypto'
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Version Number
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
major_version = "1"
|
||||||
|
minor_version = "4"
|
||||||
|
patch_version = "1"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Debug Flag (switch to False for production release code)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
debug = False
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Usage String
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
usage = """
|
||||||
|
Encrypt by explicit file path:
|
||||||
|
------------------------------
|
||||||
|
crypto <options> [file path] <file path 2...>
|
||||||
|
|
||||||
|
|
||||||
|
Encrypt all top level files in directory:
|
||||||
|
-----------------------------------------
|
||||||
|
crypto <options> [directory path] <directory path 2...>
|
||||||
|
|
||||||
|
|
||||||
|
Create a tar archive from directory and encrypt the archive:
|
||||||
|
-----------------------------------------------------------
|
||||||
|
crypto --tar [directory path] <directory path 2...>
|
||||||
|
|
||||||
|
|
||||||
|
Decrypt by explicit file path:
|
||||||
|
------------------------------
|
||||||
|
decrypto <options> [file path] <file path 2...>
|
||||||
|
|
||||||
|
|
||||||
|
Decrypt all top level encrypted files in directory:
|
||||||
|
---------------------------------------------------
|
||||||
|
decrypto <options> [directory path] <directory path 2...>
|
||||||
|
|
||||||
|
|
||||||
|
Enter `crypto --help` or `decrypto --help` to view the available options.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Help String
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
help = """
|
||||||
|
-------------------------------------------------
|
||||||
|
crypto
|
||||||
|
Simple symmetric GPG file encryption
|
||||||
|
Copyright 2015 Christopher Simpkins
|
||||||
|
MIT license
|
||||||
|
Source: https://github.com/chrissimpkins/crypto
|
||||||
|
Docs: https://chrissimpkins.github.io/crypto/
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
ABOUT
|
||||||
|
crypto provides a simple interface to symmetric Gnu Privacy Guard (gpg) encryption and decryption for one or more files. gpg must be installed on your system in order to use the crypto and decrypto executables.
|
||||||
|
|
||||||
|
USAGE
|
||||||
|
ENCRYPTION
|
||||||
|
crypto <options> [file path] <file path...>
|
||||||
|
crypto <options> [directory path] <directory path...>
|
||||||
|
|
||||||
|
DECRYPTION
|
||||||
|
decrypto <options> [file path] <file path...>
|
||||||
|
decrypto <options> [directory path] <directory path...>
|
||||||
|
|
||||||
|
CRYPTO OPTIONS
|
||||||
|
--armor | -a Use a portable ASCII armored encryption format
|
||||||
|
--hash Generate SHA256 hash digest of encrypted file(s)
|
||||||
|
--space Favor reduced file size over encryption speed
|
||||||
|
--speed Favor encryption speed over reduced file size
|
||||||
|
--tar Create tar archive of directory of files before encryption
|
||||||
|
|
||||||
|
DECRYPTO OPTIONS
|
||||||
|
--nountar Do not automatically unpack decrypted tar archives
|
||||||
|
--overwrite | -o Overwrite an existing file with the decrypted file
|
||||||
|
--stdout | -s Print file contents to the standard output stream
|
||||||
|
|
||||||
|
OTHER OPTIONS
|
||||||
|
--help | -h Display crypto and decrypto help
|
||||||
|
--usage Display crypto and decrypto usage
|
||||||
|
--version | -v Display version number
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
Use one or more explicit file path arguments to encrypt or decrypt the file(s). crypto and decrypto will attempt to encrypt or decrypt (respectively) any explicit filepaths that you include irrespective of the file type. Encrypted files are generated on the path '<original_filepath>.crypt'. The original file is not modified or removed by crypto.
|
||||||
|
|
||||||
|
Use one or more directory arguments with the crypto executable to encrypt all files in the top level of each directory with the same passphrase. Previously encrypted files with a '.crypt' file type will not be generated again in a directory. Remove them before you run the command if you intend to repeat encryption with a file.
|
||||||
|
|
||||||
|
Use one or more directory arguments with decrypto to decrypt all .crypt, .gpg, .asc, and .pgp files in the top level of each directory. decrypto automatically unpacks decrypted tar archives.
|
||||||
|
|
||||||
|
Encryption is performed with the AES256 cipher algorithm. Decryption will take place with any cipher algorithm that your version of gpg supports.
|
||||||
|
"""
|
@ -0,0 +1 @@
|
|||||||
|
pip
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue