├── .coveragerc ├── .gitignore ├── AUTHORS.rst ├── CHANGELOG.rst ├── LICENSE.txt ├── README.md ├── docs ├── Makefile ├── _static │ └── .gitignore ├── authors.rst ├── changelog.rst ├── conf.py ├── index.rst └── license.rst ├── setup.cfg ├── setup.py ├── src └── lancer │ ├── __init__.py │ ├── entry.py │ ├── fixers │ ├── __init__.py │ ├── comments.py │ └── variables.py │ ├── resources │ ├── lyrics.txt │ ├── sfw_lyrics.txt │ └── sounds.txt │ └── utils.py └── tests ├── conftest.py ├── test_CommentFixer.py └── test_VariableFixer.py /.coveragerc: -------------------------------------------------------------------------------- 1 | # .coveragerc to control coverage.py 2 | [run] 3 | branch = True 4 | source = lancer 5 | # omit = bad_file.py 6 | 7 | [paths] 8 | source = 9 | src/ 10 | */site-packages/ 11 | 12 | [report] 13 | # Regexes for lines to exclude from consideration 14 | exclude_lines = 15 | # Have to re-enable the standard pragma 16 | pragma: no cover 17 | 18 | # Don't complain about missing debug-only code: 19 | def __repr__ 20 | if self\.debug 21 | 22 | # Don't complain if tests don't hit defensive assertion code: 23 | raise AssertionError 24 | raise NotImplementedError 25 | 26 | # Don't complain if non-runnable code isn't run: 27 | if 0: 28 | if __name__ == .__main__.: 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary and binary files 2 | *~ 3 | *.py[cod] 4 | *.so 5 | *.cfg 6 | !.isort.cfg 7 | !setup.cfg 8 | *.orig 9 | *.log 10 | *.pot 11 | __pycache__/* 12 | .cache/* 13 | .*.swp 14 | */.ipynb_checkpoints/* 15 | .DS_Store 16 | 17 | # Project files 18 | .ropeproject 19 | .project 20 | .pydevproject 21 | .settings 22 | .idea 23 | tags 24 | 25 | # Package files 26 | *.egg 27 | *.eggs/ 28 | .installed.cfg 29 | *.egg-info 30 | 31 | # Unittest and coverage 32 | htmlcov/* 33 | .coverage 34 | .tox 35 | junit.xml 36 | coverage.xml 37 | .pytest_cache/ 38 | 39 | # Build and docs folder/files 40 | build/* 41 | dist/* 42 | sdist/* 43 | docs/api/* 44 | docs/_rst/* 45 | docs/_build/* 46 | cover/* 47 | MANIFEST 48 | 49 | # Per-project virtualenvs 50 | .venv*/ 51 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributors 3 | ============ 4 | 5 | * Levi Borodenko 6 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Changelog 3 | ========= 4 | 5 | Version 0.9.0 6 | =========== 7 | 8 | - Implemented variable substitution (VariableFixer class). 9 | - Implemented comment substitution (CommentFixer class). 10 | - Added CLI "lance". 11 | - Added rudimentary testing for both fixers classes. 12 | 13 | Version 0.9.1 14 | =========== 15 | 16 | - variable names now contain animal sounds. 17 | - README.md is prototyped. 18 | 19 | 20 | Version 0.9.2 21 | =========== 22 | 23 | - Added some animal sounds. 24 | - README.md is now drafted 25 | - Uploading to test-PyPi 26 | - Renaming package to py-lancer to avoid clash in PyPi 27 | 28 | 29 | Version 1.0.0 (TODO) 30 | =========== 31 | 32 | - test PyPi is working as expected. 33 | - ready for release 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Levi Borodenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lancer 2 | Ever heard of [Black](https://github.com/psf/black)? This is the opposite. 3 | A tool to turn your clean python code into a hideous (working) mess. 4 | 5 | ## Features 6 | 1. Turn all comments into Pitbull lyrics 💃 (or optionally, something safe for work) 7 | 2. Turn all your variable names into a mixture of animal sounds and horribly similar looking characters like "bark_bark_0OO0O". 🐶 8 | 3. Add irritating white spaces. 9 | 4. Add new, totally redundant comments. 10 | 5. Code still runs after all these _improvements_! 👷 11 | 12 | 13 | ## Example 14 | Before: 15 | ```python 16 | 17 | 18 | # Function that finds the sum of a list of numbers 19 | def get_sum(nums: List[int]) -> int: 20 | 21 | sum = 0 22 | 23 | # Find the sum 24 | for num in nums: 25 | sum += num 26 | 27 | # Return the sum 28 | return sum 29 | 30 | if __name__ == '__main__': 31 | sum = get_sum([1, 3]) 32 | print(f"Sum of 1 and 3 is {sum}") 33 | 34 | ``` 35 | 36 | After: 37 | ```python 38 | 39 | 40 | # Hey baby, givin' it your all when you're dancin' on me 41 | def oink_oink_oink_IlΙlll (ribbit_ααaαα :List [int ])->int : 42 | 43 | # Setting value of sum 44 | sum =0 45 | 46 | # Bada bing, bada boom 47 | for num in ribbit_ααaαα : 48 | sum +=num 49 | 50 | # there's nothing like Miami's heat 51 | return sum 52 | 53 | if __name__ =='__main__': 54 | # Setting value of sum 55 | sum =oink_oink_oink_IlΙlll ([1 ,3 ]) 56 | print (f"Sum of 1 and 3 is {sum}") 57 | 58 | ``` 59 | 60 | ## Installation and Usage 61 | Simply run `pip install py-lancer` and then use the `lance` command line tool. 62 | 63 | ``` 64 | usage: lance [-h] [--version] -f ./FILE_PATH.py [-s][-y] 65 | 66 | Ever heard of Black? This is the opposite. 67 | 68 | optional arguments: 69 | -h, --help show this help message and exit 70 | --version show program's version number and exit 71 | -f ./FILE_PATH.py, --file ./FILE_PATH.py 72 | Python file to be lance'd. 73 | -s, --sfw Generate comments that are safe for work. 74 | -y, --yolo Overwrite original file, lol. 75 | ``` 76 | 77 | So if you have a python file at `./test.py`, you simply run `lance -f ./test.py` 78 | 79 | ## How does it work 80 | The key tool we use it the `tokenizer` standard module in python. It allows us to tokenize any python script which then in turn makes substituting comments and variable names fairly simple. 81 | Check out the source code for more details. 82 | 83 | ## Contribute 84 | Bug reports, fixes and additional features are always welcome! Make sure to run the tests with `python setup.py test` and write your own for new features. Thanks. 85 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = ../build/sphinx/ 9 | AUTODOCDIR = api 10 | AUTODOCBUILD = sphinx-apidoc 11 | PROJECT = lancer 12 | MODULEDIR = ../src/lancer 13 | 14 | # User-friendly check for sphinx-build 15 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $?), 1) 16 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 17 | endif 18 | 19 | # Internal variables. 20 | PAPEROPT_a4 = -D latex_paper_size=a4 21 | PAPEROPT_letter = -D latex_paper_size=letter 22 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 23 | # the i18n builder cannot share the environment and doctrees with the others 24 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 25 | 26 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext doc-requirements 27 | 28 | help: 29 | @echo "Please use \`make ' where is one of" 30 | @echo " html to make standalone HTML files" 31 | @echo " dirhtml to make HTML files named index.html in directories" 32 | @echo " singlehtml to make a single large HTML file" 33 | @echo " pickle to make pickle files" 34 | @echo " json to make JSON files" 35 | @echo " htmlhelp to make HTML files and a HTML help project" 36 | @echo " qthelp to make HTML files and a qthelp project" 37 | @echo " devhelp to make HTML files and a Devhelp project" 38 | @echo " epub to make an epub" 39 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 40 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 41 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 42 | @echo " text to make text files" 43 | @echo " man to make manual pages" 44 | @echo " texinfo to make Texinfo files" 45 | @echo " info to make Texinfo files and run them through makeinfo" 46 | @echo " gettext to make PO message catalogs" 47 | @echo " changes to make an overview of all changed/added/deprecated items" 48 | @echo " xml to make Docutils-native XML files" 49 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 50 | @echo " linkcheck to check all external links for integrity" 51 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 52 | 53 | clean: 54 | rm -rf $(BUILDDIR)/* $(AUTODOCDIR) 55 | 56 | $(AUTODOCDIR): $(MODULEDIR) 57 | mkdir -p $@ 58 | $(AUTODOCBUILD) -f -o $@ $^ 59 | 60 | doc-requirements: $(AUTODOCDIR) 61 | 62 | html: doc-requirements 63 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 64 | @echo 65 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 66 | 67 | dirhtml: doc-requirements 68 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 69 | @echo 70 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 71 | 72 | singlehtml: doc-requirements 73 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 74 | @echo 75 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 76 | 77 | pickle: doc-requirements 78 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 79 | @echo 80 | @echo "Build finished; now you can process the pickle files." 81 | 82 | json: doc-requirements 83 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 84 | @echo 85 | @echo "Build finished; now you can process the JSON files." 86 | 87 | htmlhelp: doc-requirements 88 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 89 | @echo 90 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 91 | ".hhp project file in $(BUILDDIR)/htmlhelp." 92 | 93 | qthelp: doc-requirements 94 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 95 | @echo 96 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 97 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 98 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/$(PROJECT).qhcp" 99 | @echo "To view the help file:" 100 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/$(PROJECT).qhc" 101 | 102 | devhelp: doc-requirements 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $HOME/.local/share/devhelp/$(PROJECT)" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $HOME/.local/share/devhelp/$(PROJEC)" 109 | @echo "# devhelp" 110 | 111 | epub: doc-requirements 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | patch-latex: 117 | find _build/latex -iname "*.tex" | xargs -- \ 118 | sed -i'' 's~includegraphics{~includegraphics\[keepaspectratio,max size={\\textwidth}{\\textheight}\]{~g' 119 | 120 | latex: doc-requirements 121 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 122 | $(MAKE) patch-latex 123 | @echo 124 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 125 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 126 | "(use \`make latexpdf' here to do that automatically)." 127 | 128 | latexpdf: doc-requirements 129 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 130 | $(MAKE) patch-latex 131 | @echo "Running LaTeX files through pdflatex..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | latexpdfja: doc-requirements 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo "Running LaTeX files through platex and dvipdfmx..." 138 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 139 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 140 | 141 | text: doc-requirements 142 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 143 | @echo 144 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 145 | 146 | man: doc-requirements 147 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 148 | @echo 149 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 150 | 151 | texinfo: doc-requirements 152 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 153 | @echo 154 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 155 | @echo "Run \`make' in that directory to run these through makeinfo" \ 156 | "(use \`make info' here to do that automatically)." 157 | 158 | info: doc-requirements 159 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 160 | @echo "Running Texinfo files through makeinfo..." 161 | make -C $(BUILDDIR)/texinfo info 162 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 163 | 164 | gettext: doc-requirements 165 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 166 | @echo 167 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 168 | 169 | changes: doc-requirements 170 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 171 | @echo 172 | @echo "The overview file is in $(BUILDDIR)/changes." 173 | 174 | linkcheck: doc-requirements 175 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 176 | @echo 177 | @echo "Link check complete; look for any errors in the above output " \ 178 | "or in $(BUILDDIR)/linkcheck/output.txt." 179 | 180 | doctest: doc-requirements 181 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 182 | @echo "Testing of doctests in the sources finished, look at the " \ 183 | "results in $(BUILDDIR)/doctest/output.txt." 184 | 185 | xml: doc-requirements 186 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 187 | @echo 188 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 189 | 190 | pseudoxml: doc-requirements 191 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 192 | @echo 193 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 194 | -------------------------------------------------------------------------------- /docs/_static/.gitignore: -------------------------------------------------------------------------------- 1 | # Empty directory 2 | -------------------------------------------------------------------------------- /docs/authors.rst: -------------------------------------------------------------------------------- 1 | .. _authors: 2 | .. include:: ../AUTHORS.rst 3 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. _changes: 2 | .. include:: ../CHANGELOG.rst 3 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # This file is execfile()d with the current directory set to its containing dir. 4 | # 5 | # Note that not all possible configuration values are present in this 6 | # autogenerated file. 7 | # 8 | # All configuration values have a default; values that are commented out 9 | # serve to show the default. 10 | 11 | import os 12 | import sys 13 | import inspect 14 | import shutil 15 | 16 | __location__ = os.path.join(os.getcwd(), os.path.dirname( 17 | inspect.getfile(inspect.currentframe()))) 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | sys.path.insert(0, os.path.join(__location__, '../src')) 23 | 24 | # -- Run sphinx-apidoc ------------------------------------------------------ 25 | # This hack is necessary since RTD does not issue `sphinx-apidoc` before running 26 | # `sphinx-build -b html . _build/html`. See Issue: 27 | # https://github.com/rtfd/readthedocs.org/issues/1139 28 | # DON'T FORGET: Check the box "Install your project inside a virtualenv using 29 | # setup.py install" in the RTD Advanced Settings. 30 | # Additionally it helps us to avoid running apidoc manually 31 | 32 | try: # for Sphinx >= 1.7 33 | from sphinx.ext import apidoc 34 | except ImportError: 35 | from sphinx import apidoc 36 | 37 | output_dir = os.path.join(__location__, "api") 38 | module_dir = os.path.join(__location__, "../src/lancer") 39 | try: 40 | shutil.rmtree(output_dir) 41 | except FileNotFoundError: 42 | pass 43 | 44 | try: 45 | import sphinx 46 | from pkg_resources import parse_version 47 | 48 | cmd_line_template = "sphinx-apidoc -f -o {outputdir} {moduledir}" 49 | cmd_line = cmd_line_template.format(outputdir=output_dir, moduledir=module_dir) 50 | 51 | args = cmd_line.split(" ") 52 | if parse_version(sphinx.__version__) >= parse_version('1.7'): 53 | args = args[1:] 54 | 55 | apidoc.main(args) 56 | except Exception as e: 57 | print("Running `sphinx-apidoc` failed!\n{}".format(e)) 58 | 59 | # -- General configuration ----------------------------------------------------- 60 | 61 | # If your documentation needs a minimal Sphinx version, state it here. 62 | # needs_sphinx = '1.0' 63 | 64 | # Add any Sphinx extension module names here, as strings. They can be extensions 65 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 66 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 67 | 'sphinx.ext.autosummary', 'sphinx.ext.viewcode', 'sphinx.ext.coverage', 68 | 'sphinx.ext.doctest', 'sphinx.ext.ifconfig', 'sphinx.ext.mathjax', 69 | 'sphinx.ext.napoleon'] 70 | 71 | # Add any paths that contain templates here, relative to this directory. 72 | templates_path = ['_templates'] 73 | 74 | 75 | # To configure AutoStructify 76 | def setup(app): 77 | from recommonmark.transform import AutoStructify 78 | app.add_config_value('recommonmark_config', { 79 | 'auto_toc_tree_section': 'Contents', 80 | 'enable_eval_rst': True, 81 | 'enable_auto_doc_ref': True, 82 | 'enable_math': True, 83 | 'enable_inline_math': True 84 | }, True) 85 | app.add_transform(AutoStructify) 86 | 87 | # Additional parsers besides rst 88 | source_parsers = { 89 | '.md': 'recommonmark.parser.CommonMarkParser', 90 | } 91 | 92 | # The suffix of source filenames. 93 | source_suffix = ['.rst', '.md'] 94 | 95 | # The encoding of source files. 96 | # source_encoding = 'utf-8-sig' 97 | 98 | # The master toctree document. 99 | master_doc = 'index' 100 | 101 | # General information about the project. 102 | project = u'lancer' 103 | copyright = u'2019, Levi Borodenko' 104 | 105 | # The version info for the project you're documenting, acts as replacement for 106 | # |version| and |release|, also used in various other places throughout the 107 | # built documents. 108 | # 109 | # The short X.Y version. 110 | version = '' # Is set by calling `setup.py docs` 111 | # The full version, including alpha/beta/rc tags. 112 | release = '' # Is set by calling `setup.py docs` 113 | 114 | # The language for content autogenerated by Sphinx. Refer to documentation 115 | # for a list of supported languages. 116 | # language = None 117 | 118 | # There are two options for replacing |today|: either, you set today to some 119 | # non-false value, then it is used: 120 | # today = '' 121 | # Else, today_fmt is used as the format for a strftime call. 122 | # today_fmt = '%B %d, %Y' 123 | 124 | # List of patterns, relative to source directory, that match files and 125 | # directories to ignore when looking for source files. 126 | exclude_patterns = ['_build'] 127 | 128 | # The reST default role (used for this markup: `text`) to use for all documents. 129 | # default_role = None 130 | 131 | # If true, '()' will be appended to :func: etc. cross-reference text. 132 | # add_function_parentheses = True 133 | 134 | # If true, the current module name will be prepended to all description 135 | # unit titles (such as .. function::). 136 | # add_module_names = True 137 | 138 | # If true, sectionauthor and moduleauthor directives will be shown in the 139 | # output. They are ignored by default. 140 | # show_authors = False 141 | 142 | # The name of the Pygments (syntax highlighting) style to use. 143 | pygments_style = 'sphinx' 144 | 145 | # A list of ignored prefixes for module index sorting. 146 | # modindex_common_prefix = [] 147 | 148 | # If true, keep warnings as "system message" paragraphs in the built documents. 149 | # keep_warnings = False 150 | 151 | 152 | # -- Options for HTML output --------------------------------------------------- 153 | 154 | # The theme to use for HTML and HTML Help pages. See the documentation for 155 | # a list of builtin themes. 156 | html_theme = 'alabaster' 157 | 158 | # Theme options are theme-specific and customize the look and feel of a theme 159 | # further. For a list of options available for each theme, see the 160 | # documentation. 161 | html_theme_options = { 162 | 'sidebar_width': '300px', 163 | 'page_width': '1200px' 164 | } 165 | 166 | # Add any paths that contain custom themes here, relative to this directory. 167 | # html_theme_path = [] 168 | 169 | # The name for this set of Sphinx documents. If None, it defaults to 170 | # " v documentation". 171 | try: 172 | from lancer import __version__ as version 173 | except ImportError: 174 | pass 175 | else: 176 | release = version 177 | 178 | # A shorter title for the navigation bar. Default is the same as html_title. 179 | # html_short_title = None 180 | 181 | # The name of an image file (relative to this directory) to place at the top 182 | # of the sidebar. 183 | # html_logo = "" 184 | 185 | # The name of an image file (within the static path) to use as favicon of the 186 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 187 | # pixels large. 188 | # html_favicon = None 189 | 190 | # Add any paths that contain custom static files (such as style sheets) here, 191 | # relative to this directory. They are copied after the builtin static files, 192 | # so a file named "default.css" will overwrite the builtin "default.css". 193 | html_static_path = ['_static'] 194 | 195 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 196 | # using the given strftime format. 197 | # html_last_updated_fmt = '%b %d, %Y' 198 | 199 | # If true, SmartyPants will be used to convert quotes and dashes to 200 | # typographically correct entities. 201 | # html_use_smartypants = True 202 | 203 | # Custom sidebar templates, maps document names to template names. 204 | # html_sidebars = {} 205 | 206 | # Additional templates that should be rendered to pages, maps page names to 207 | # template names. 208 | # html_additional_pages = {} 209 | 210 | # If false, no module index is generated. 211 | # html_domain_indices = True 212 | 213 | # If false, no index is generated. 214 | # html_use_index = True 215 | 216 | # If true, the index is split into individual pages for each letter. 217 | # html_split_index = False 218 | 219 | # If true, links to the reST sources are added to the pages. 220 | # html_show_sourcelink = True 221 | 222 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 223 | # html_show_sphinx = True 224 | 225 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 226 | # html_show_copyright = True 227 | 228 | # If true, an OpenSearch description file will be output, and all pages will 229 | # contain a tag referring to it. The value of this option must be the 230 | # base URL from which the finished HTML is served. 231 | # html_use_opensearch = '' 232 | 233 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 234 | # html_file_suffix = None 235 | 236 | # Output file base name for HTML help builder. 237 | htmlhelp_basename = 'lancer-doc' 238 | 239 | 240 | # -- Options for LaTeX output -------------------------------------------------- 241 | 242 | latex_elements = { 243 | # The paper size ('letterpaper' or 'a4paper'). 244 | # 'papersize': 'letterpaper', 245 | 246 | # The font size ('10pt', '11pt' or '12pt'). 247 | # 'pointsize': '10pt', 248 | 249 | # Additional stuff for the LaTeX preamble. 250 | # 'preamble': '', 251 | } 252 | 253 | # Grouping the document tree into LaTeX files. List of tuples 254 | # (source start file, target name, title, author, documentclass [howto/manual]). 255 | latex_documents = [ 256 | ('index', 'user_guide.tex', u'lancer Documentation', 257 | u'Levi Borodenko', 'manual'), 258 | ] 259 | 260 | # The name of an image file (relative to this directory) to place at the top of 261 | # the title page. 262 | # latex_logo = "" 263 | 264 | # For "manual" documents, if this is true, then toplevel headings are parts, 265 | # not chapters. 266 | # latex_use_parts = False 267 | 268 | # If true, show page references after internal links. 269 | # latex_show_pagerefs = False 270 | 271 | # If true, show URL addresses after external links. 272 | # latex_show_urls = False 273 | 274 | # Documents to append as an appendix to all manuals. 275 | # latex_appendices = [] 276 | 277 | # If false, no module index is generated. 278 | # latex_domain_indices = True 279 | 280 | # -- External mapping ------------------------------------------------------------ 281 | python_version = '.'.join(map(str, sys.version_info[0:2])) 282 | intersphinx_mapping = { 283 | 'sphinx': ('http://www.sphinx-doc.org/en/stable', None), 284 | 'python': ('https://docs.python.org/' + python_version, None), 285 | 'matplotlib': ('https://matplotlib.org', None), 286 | 'numpy': ('https://docs.scipy.org/doc/numpy', None), 287 | 'sklearn': ('http://scikit-learn.org/stable', None), 288 | 'pandas': ('http://pandas.pydata.org/pandas-docs/stable', None), 289 | 'scipy': ('https://docs.scipy.org/doc/scipy/reference', None), 290 | } -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ====== 2 | lancer 3 | ====== 4 | 5 | This is the documentation of **lancer**. 6 | 7 | .. note:: 8 | 9 | This is the main page of your project's `Sphinx`_ documentation. 10 | It is formatted in `reStructuredText`_. Add additional pages 11 | by creating rst-files in ``docs`` and adding them to the `toctree`_ below. 12 | Use then `references`_ in order to link them from this page, e.g. 13 | :ref:`authors` and :ref:`changes`. 14 | 15 | It is also possible to refer to the documentation of other Python packages 16 | with the `Python domain syntax`_. By default you can reference the 17 | documentation of `Sphinx`_, `Python`_, `NumPy`_, `SciPy`_, `matplotlib`_, 18 | `Pandas`_, `Scikit-Learn`_. You can add more by extending the 19 | ``intersphinx_mapping`` in your Sphinx's ``conf.py``. 20 | 21 | The pretty useful extension `autodoc`_ is activated by default and lets 22 | you include documentation from docstrings. Docstrings can be written in 23 | `Google style`_ (recommended!), `NumPy style`_ and `classical style`_. 24 | 25 | 26 | Contents 27 | ======== 28 | 29 | .. toctree:: 30 | :maxdepth: 2 31 | 32 | License 33 | Authors 34 | Changelog 35 | Module Reference 36 | 37 | 38 | Indices and tables 39 | ================== 40 | 41 | * :ref:`genindex` 42 | * :ref:`modindex` 43 | * :ref:`search` 44 | 45 | .. _toctree: http://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html 46 | .. _reStructuredText: http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html 47 | .. _references: http://www.sphinx-doc.org/en/stable/markup/inline.html 48 | .. _Python domain syntax: http://sphinx-doc.org/domains.html#the-python-domain 49 | .. _Sphinx: http://www.sphinx-doc.org/ 50 | .. _Python: http://docs.python.org/ 51 | .. _Numpy: http://docs.scipy.org/doc/numpy 52 | .. _SciPy: http://docs.scipy.org/doc/scipy/reference/ 53 | .. _matplotlib: https://matplotlib.org/contents.html# 54 | .. _Pandas: http://pandas.pydata.org/pandas-docs/stable 55 | .. _Scikit-Learn: http://scikit-learn.org/stable 56 | .. _autodoc: http://www.sphinx-doc.org/en/stable/ext/autodoc.html 57 | .. _Google style: https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings 58 | .. _NumPy style: https://numpydoc.readthedocs.io/en/latest/format.html 59 | .. _classical style: http://www.sphinx-doc.org/en/stable/domains.html#info-field-lists 60 | -------------------------------------------------------------------------------- /docs/license.rst: -------------------------------------------------------------------------------- 1 | .. _license: 2 | 3 | ======= 4 | License 5 | ======= 6 | 7 | .. include:: ../LICENSE.txt 8 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | # This file is used to configure your project. 2 | # Read more about the various options under: 3 | # http://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files 4 | 5 | [metadata] 6 | name = py-lancer 7 | description = A tool to turn your clean python code into a hideous mess. 8 | author = Levi Borodenko 9 | author-email = Levi.borodenko@gmail.com 10 | license = mit 11 | long-description = file: README.md 12 | long-description-content-type = text/markdown; charset=UTF-8 13 | # url = https://github.com/pyscaffold/pyscaffold/ 14 | project-urls = 15 | GitHub = https://github.com/LeviBorodenko/lancer 16 | # Change if running only on Windows, Mac or Linux (comma-separated) 17 | platforms = any 18 | # Add here all kinds of additional classifiers as defined under 19 | # https://pypi.python.org/pypi?%3Aaction=list_classifiers 20 | classifiers = 21 | Development Status :: 4 - Beta 22 | Programming Language :: Python 23 | 24 | [options] 25 | zip_safe = False 26 | packages = find: 27 | include_package_data = True 28 | package_dir = 29 | =src 30 | # DON'T CHANGE THE FOLLOWING LINE! IT WILL BE UPDATED BY PYSCAFFOLD! 31 | setup_requires = pyscaffold>=3.2a0,<3.3a0 32 | # Add here dependencies of your project (semicolon/line-separated), e.g. 33 | # install_requires = numpy; scipy 34 | # The usage of test_requires is discouraged, see `Dependency Management` docs 35 | # tests_require = pytest; pytest-cov 36 | # Require a specific Python version, e.g. Python 2.7 or >= 3.4 37 | # python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* 38 | 39 | [options.packages.find] 40 | where = src 41 | exclude = 42 | tests 43 | 44 | [options.extras_require] 45 | # Add here additional requirements for extra features, to install with: 46 | # `pip install lancer[PDF]` like: 47 | # PDF = ReportLab; RXP 48 | # Add here test requirements (semicolon/line-separated) 49 | testing = 50 | pytest 51 | pytest-cov 52 | 53 | [options.entry_points] 54 | # Add here console scripts like: 55 | # console_scripts = 56 | # script_name = lancer.module:function 57 | # For example: 58 | console_scripts = 59 | lance = lancer.entry:run 60 | # And any other entry points, for example: 61 | # pyscaffold.cli = 62 | # awesome = pyscaffoldext.awesome.extension:AwesomeExtension 63 | 64 | [test] 65 | # py.test options when running `python setup.py test` 66 | # addopts = --verbose 67 | extras = True 68 | 69 | [tool:pytest] 70 | # Options for py.test: 71 | # Specify command line options as you would do when invoking py.test directly. 72 | # e.g. --cov-report html (or xml) for html/xml output or --junitxml junit.xml 73 | # in order to write a coverage file that can be read by Jenkins. 74 | addopts = 75 | --cov lancer --cov-report term-missing 76 | --verbose 77 | norecursedirs = 78 | dist 79 | build 80 | .tox 81 | testpaths = tests 82 | 83 | [aliases] 84 | dists = bdist_wheel 85 | 86 | [bdist_wheel] 87 | # Use this option if your package is pure-python 88 | universal = 1 89 | 90 | [build_sphinx] 91 | source_dir = docs 92 | build_dir = build/sphinx 93 | 94 | [devpi:upload] 95 | # Options for the devpi: PyPI server and packaging tool 96 | # VCS export must be deactivated since we are using setuptools-scm 97 | no-vcs = 1 98 | formats = bdist_wheel 99 | 100 | [flake8] 101 | # Some sane defaults for the code style checker flake8 102 | exclude = 103 | .tox 104 | build 105 | dist 106 | .eggs 107 | docs/conf.py 108 | 109 | [pyscaffold] 110 | # PyScaffold's parameters when the project was created. 111 | # This will be used when updating. Do not change! 112 | version = 3.2.2 113 | package = lancer 114 | extensions = 115 | markdown 116 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Setup file for lancer. 4 | Use setup.cfg to configure your project. 5 | 6 | """ 7 | import sys 8 | 9 | from pkg_resources import VersionConflict, require 10 | from setuptools import setup 11 | 12 | try: 13 | require('setuptools>=38.3') 14 | except VersionConflict: 15 | print("Error: version of setuptools is too old (<38.3)!") 16 | sys.exit(1) 17 | 18 | 19 | if __name__ == "__main__": 20 | setup(use_pyscaffold=True) 21 | -------------------------------------------------------------------------------- /src/lancer/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from pkg_resources import get_distribution, DistributionNotFound 3 | 4 | try: 5 | # Change here if project is renamed and does not equal the package name 6 | dist_name = __name__ 7 | __version__ = get_distribution(dist_name).version 8 | except DistributionNotFound: 9 | __version__ = 'unknown' 10 | finally: 11 | del get_distribution, DistributionNotFound 12 | -------------------------------------------------------------------------------- /src/lancer/entry.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Then run `python setup.py install` which will install the command `lance` 4 | inside your current environment. 5 | """ 6 | 7 | import argparse 8 | import sys 9 | import logging 10 | from pathlib import Path 11 | from lancer.utils import copy_and_delete 12 | 13 | from lancer.fixers.comments import CommentFixer 14 | from lancer.fixers.variables import VariableFixer 15 | 16 | from lancer import __version__ 17 | 18 | __author__ = "Levi Borodenko" 19 | __copyright__ = "Levi Borodenko" 20 | __license__ = "mit" 21 | 22 | _logger = logging.getLogger(__name__) 23 | 24 | 25 | def parse_args(args): 26 | """Parse command line parameters 27 | 28 | Args: 29 | args ([str]): command line parameters as list of strings 30 | 31 | Returns: 32 | :obj:`argparse.Namespace`: command line parameters namespace 33 | """ 34 | parser = argparse.ArgumentParser( 35 | description="Ever heard of Black? This is the opposite.") 36 | parser.add_argument( 37 | "--version", 38 | action="version", 39 | version="lancer {ver}".format(ver=__version__)) 40 | parser.add_argument( 41 | "-f", 42 | "--file", 43 | dest="file", 44 | help="Python file to be lance'd.", 45 | type=Path, 46 | action="store", 47 | required=True, 48 | metavar="./FILE_PATH.py") 49 | parser.add_argument( 50 | "-s", 51 | "--sfw", 52 | dest="sfw", 53 | help="Generate comments that are safe for work.", 54 | action="store_true") 55 | parser.add_argument( 56 | "-y", 57 | "--yolo", 58 | dest="yolo", 59 | help="Overwrite original file, lol.", 60 | action="store_true") 61 | return parser.parse_args(args) 62 | 63 | 64 | def setup_logging(loglevel): 65 | """Setup basic logging 66 | 67 | Args: 68 | loglevel (int): minimum loglevel for emitting messages 69 | """ 70 | logformat = "[%(asctime)s] %(levelname)s:%(name)s:%(message)s" 71 | logging.basicConfig(level=loglevel, stream=sys.stdout, 72 | format=logformat, datefmt="%Y-%m-%d %H:%M:%S") 73 | 74 | 75 | def lance(file: Path = "./file.py", sfw: bool = False, yolo: bool = False): 76 | """[summary] 77 | Takes a file and lances it. 78 | 79 | [description] 80 | Turns our code into the most horrendous mess imaginable. 81 | Seriously. 82 | 83 | Keyword Arguments: 84 | file {Path} -- File to be lanced (default: {"./file.py"}) 85 | sfw {bool} -- Generate comments that are safe for work if true (default: {False}) 86 | yolo {bool} -- Overwrites original if true (default: {False}) 87 | """ 88 | # turn file into path if not already 89 | file = Path(file) 90 | 91 | # initiate "fixers" 92 | variable_fixer = VariableFixer() 93 | comment_fixer = CommentFixer() 94 | comment_fixer.sfw = sfw 95 | 96 | # First fix variables. This must be done before fixing comments as the 97 | # comment fixer adds comments referencing variable names. 98 | variable_fixer.fix(file) 99 | 100 | # get the output file 101 | fixed_file = variable_fixer.__output__ 102 | 103 | # Now fix comments 104 | comment_fixer.fix(fixed_file) 105 | 106 | # if yolo mode is true, substitute original 107 | if yolo: 108 | copy_and_delete(fixed_file, file) 109 | 110 | 111 | def main(args): 112 | """Main entry point allowing external calls 113 | 114 | Args: 115 | args ([str]): command line parameter list 116 | """ 117 | args = parse_args(args) 118 | 119 | lance(file=args.file, sfw=args.sfw, yolo=args.yolo) 120 | 121 | 122 | def run(): 123 | """Entry point for console_scripts 124 | """ 125 | main(sys.argv[1:]) 126 | 127 | 128 | if __name__ == "__main__": 129 | run() 130 | -------------------------------------------------------------------------------- /src/lancer/fixers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LeviBorodenko/lancer/0274934665b1403f67d25b34c81f0c8a3c7dd36a/src/lancer/fixers/__init__.py -------------------------------------------------------------------------------- /src/lancer/fixers/comments.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from tokenize import COMMENT, NAME, NEWLINE, OP 3 | from typing import List, Sequence, Tuple 4 | from random import randint 5 | from lancer.utils import fix_wrapper, isemptytype, window 6 | import pkg_resources 7 | 8 | __author__ = "Levi Borodenko" 9 | __copyright__ = "Levi Borodenko" 10 | __license__ = "mit" 11 | 12 | 13 | # Tuple of (, () 14 | # These tokens are returned by the fixer, and are suitable for being passed to 15 | # untokenize() to convert back to source code. 16 | LancerTokenType = Tuple[int, str] 17 | 18 | 19 | # setting up logger instance 20 | _logger = logging.getLogger(__name__) 21 | 22 | 23 | class CommentFixer(object): 24 | """[summary] 25 | Turns all comments in a file into Pitbull song lyrics. 26 | 27 | [description] 28 | Iterates over all tokens, substituting the "COMMENT" 29 | ones with its own. 30 | 31 | Methods: 32 | fix(file) - takes file and returns the fixed "file.lanced" 33 | """ 34 | 35 | def __init__(self): 36 | super(CommentFixer, self).__init__() 37 | 38 | # Path to lyric file resources 39 | self.LYRIC_FILE = pkg_resources.resource_filename( 40 | __name__, "../resources/lyrics.txt") 41 | 42 | self.SFW_LYRIC_FILE = pkg_resources.resource_filename( 43 | __name__, "../resources/sfw_lyrics.txt") 44 | 45 | # Generate safe for work content (False by default) 46 | self.sfw = False 47 | 48 | # Number of lyrics 49 | self.NUM_LYRICS = sum(1 for line in open(self.LYRIC_FILE)) 50 | 51 | # setting name 52 | self.__name__ = "CommentFixer" 53 | 54 | def _get_lyric(self) -> str: 55 | """Returns a random song lyric. 56 | 57 | By default, gives one of many insightful Pitbull quotes. 58 | If self.sfw is True, return lyrics that are safe for work. 59 | """ 60 | lyric_file = self.SFW_LYRIC_FILE if self.sfw else self.LYRIC_FILE 61 | # Open lyrics file and grab a random line 62 | with open(lyric_file) as f: 63 | 64 | random_index = randint(0, self.NUM_LYRICS - 1) 65 | 66 | lyrics = f.readlines() 67 | 68 | # .rstrip to remove trailing whitespace 69 | return "# " + lyrics[random_index].rstrip() 70 | 71 | @staticmethod 72 | def _is_comment_shebang_line(comment: str) -> bool: 73 | """ 74 | Check if a comment is a shebang line. 75 | 76 | Arguments: 77 | comment -- the comment to check 78 | 79 | Returns: 80 | result - bool indicating if comment is a shebang 81 | """ 82 | return comment.startswith("#!") 83 | 84 | def _substitute_comments_for_lyrics( 85 | self, tokens: Sequence[LancerTokenType] 86 | ) -> List[LancerTokenType]: 87 | """ 88 | Substitute any comment tokens in the sequence tokens with random 89 | lyrics. 90 | 91 | Arguments: 92 | tokens -- the list of tokens to substitute comments from. 93 | 94 | Return: 95 | out_tokens -- the new list of tokens after substitution. 96 | """ 97 | out_tokens: List[LancerTokenType] = [] 98 | 99 | for token_type, token_val in tokens: 100 | if ( 101 | token_type == COMMENT and 102 | not self._is_comment_shebang_line(token_val) 103 | ): 104 | out_tokens.append((COMMENT, self._get_lyric())) 105 | else: 106 | out_tokens.append((token_type, token_val)) 107 | 108 | return out_tokens 109 | 110 | def _add_pointless_variable_init_comments( 111 | self, tokens: Sequence[LancerTokenType] 112 | ) -> List[LancerTokenType]: 113 | """ 114 | Add totally useless comments describing when variables are initialized. 115 | E.g. my_val = 3 116 | -> 117 | # Setting value of my_val 118 | my_val = 3 119 | 120 | Arguments: 121 | tokens -- the current list of tokens for the source code. 122 | 123 | Return: 124 | out_tokens -- the new list of tokens after adding pointless 125 | comments. 126 | """ 127 | # Identify initialized variables by looking for the following 128 | # pattern: 129 | # first NEWLINE '\n' 130 | # middle NAME 'my_var' 131 | # last OP '=' 132 | # 133 | # Obviously this doesn't catch all initialized variables, but it will 134 | # catch a decent number while keeping things simple. 135 | out_tokens: List[LancerTokenType] = [] 136 | 137 | # Loop over token windows of size 3, inserting a pointless comment 138 | # if the above pattern is spotted, and adding the middle token to the 139 | # output list. Note that this means the first & last tokens are added 140 | # seperately. 141 | out_tokens.append(tokens[0]) 142 | 143 | token_iter = iter(tokens) 144 | for first, middle, last in window(token_iter, 3): 145 | if ( 146 | isemptytype(first[0]) and 147 | middle[0] == NAME and 148 | last[0] == OP 149 | and last[1] == "=" 150 | ): 151 | out_tokens.append( 152 | (COMMENT, "# Setting value of {}".format(middle[1])) 153 | ) 154 | out_tokens.append((NEWLINE, "\n")) 155 | 156 | out_tokens.append(middle) 157 | 158 | out_tokens.append(tokens[-1]) 159 | 160 | return out_tokens 161 | 162 | @fix_wrapper 163 | def fix(self, tokens): 164 | """After decoration, it will take the file that you want to fix and 165 | create a fixed .lanced file. 166 | 167 | BEFORE DECORATION: takes a list of tokens and returns 168 | a list of the fixed tokens. 169 | 170 | Arguments: 171 | tokens -- the list of tokens from the file 172 | 173 | Returns: 174 | fixed_tokens - list of tokens, but now with fixed comments 175 | """ 176 | 177 | # Strip off the unneeded token elements. 178 | stripped_tokens = [(token[0], token[1]) for token in tokens] 179 | 180 | fixed_tokens = self._substitute_comments_for_lyrics(stripped_tokens) 181 | 182 | fixed_tokens = self._add_pointless_variable_init_comments(fixed_tokens) 183 | 184 | return fixed_tokens 185 | -------------------------------------------------------------------------------- /src/lancer/fixers/variables.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from tokenize import NAME 3 | from itertools import tee 4 | import random 5 | from lancer.utils import fix_wrapper, window, isbuildin, isemptytype 6 | import pkg_resources 7 | 8 | # import pkg_resources 9 | 10 | __author__ = "Levi Borodenko" 11 | __copyright__ = "Levi Borodenko" 12 | __license__ = "mit" 13 | 14 | # setting up logger instance 15 | _logger = logging.getLogger(__name__) 16 | 17 | 18 | class VariableFixer(object): 19 | """[summary] 20 | Changes all your good variable names into horrible ones. 21 | 22 | [description] 23 | Iterates over file spotting all variable, function and class names, 24 | substituting them with its own BAD ones. 25 | 26 | Methods: 27 | fix(file) - takes file and returns the fixed "file.lanced" 28 | 29 | Todo: 30 | Fix annotation compatability 31 | """ 32 | 33 | def __init__(self): 34 | super(VariableFixer, self).__init__() 35 | 36 | # setting name 37 | self.__name__ = "VariableFixer" 38 | 39 | # dictionary containing the patterns to generate the new names 40 | self.PATTERNS = {1: {"initial": "O", "chars": ["0", "O", "Ο"]}, 41 | 2: {"initial": "I", "chars": ["I", "l", "Ι", "1"]}, 42 | 3: {"initial": None, "chars": ["α", "a"]} 43 | } 44 | 45 | # initializing dict that will translate the actual variable names 46 | # to our generated onces. 47 | self.dict = {} 48 | 49 | # length of variable name noise 50 | self.NUM_NOISE_CHAR = 5 51 | 52 | # Path to lyric file resource 53 | self.SOUNDS_FILE = pkg_resources.resource_filename( 54 | __name__, "../resources/sounds.txt") 55 | 56 | # Number of Sounds 57 | self.NUM_SOUNDS = sum(1 for line in open(self.SOUNDS_FILE)) 58 | 59 | def _get_random_noise(self) -> str: 60 | """[summary] 61 | Returns a random horrible mess according to self.PATTERNS 62 | 63 | [description] 64 | Stuff like: IllIlI11Ι1IIIlI1, 000O00OOO, aaαaaααααaaaαaα etc 65 | """ 66 | # choosing a pattern at random 67 | idx = random.randint(1, len(self.PATTERNS)) 68 | 69 | pattern = self.PATTERNS[idx] 70 | 71 | # creating noise from pattern 72 | noise = "" 73 | 74 | if pattern["initial"] is not None: 75 | noise += pattern["initial"] 76 | 77 | # generate noise from list of chars 78 | noise += "".join(random.choices(pattern["chars"], 79 | k=self.NUM_NOISE_CHAR)) 80 | 81 | return noise 82 | 83 | def _get_random_animal_sound(self) -> str: 84 | """[summary] 85 | returns a random animal sound like "bark_bark" etc. 86 | [description] 87 | We read the animal sounds from /resource/sounds.txt 88 | """ 89 | 90 | # Open sounds file and grab a random line 91 | with open(self.SOUNDS_FILE) as f: 92 | 93 | random_index = random.randint(0, self.NUM_SOUNDS - 1) 94 | 95 | sounds = f.readlines() 96 | 97 | # get random sound and strip whitespaces 98 | sound = sounds[random_index].rstrip() 99 | 100 | # repeat sound up to 2 times 101 | sound_list = [sound for i in range(random.randint(1, 3))] 102 | 103 | # join them to one string 104 | sound = "_".join(sound_list) + "_" 105 | 106 | return sound 107 | 108 | def _get_new_name(self, input_name: str): 109 | 110 | # first check if we haven't already generated a new name 111 | try: 112 | self.dict[input_name] 113 | 114 | except KeyError: 115 | 116 | # Generate new name 117 | ### 118 | 119 | sounds = self._get_random_animal_sound() 120 | noise = self._get_random_noise() 121 | 122 | # combine to generate variable name 123 | name = sounds + noise 124 | 125 | # save to dict 126 | self.dict[input_name] = name 127 | 128 | return name 129 | 130 | finally: 131 | 132 | return self.dict[input_name] 133 | 134 | def _spot_definitions(self, token_triple: iter) -> None: 135 | """[summary] 136 | Looks for function and class names in a token triple 137 | 138 | [description] 139 | Takes a consecutive token triple and checks if the middle one 140 | if is: 141 | 142 | def NAME(...): 143 | 144 | class NAME(...): 145 | 146 | Arguments: 147 | token_triple {iter} -- consecutive token triple 148 | """ 149 | # get tokens 150 | first, middle, last = token_triple 151 | 152 | # check if definition 153 | if first.string in ["def", "class"] and middle.type == NAME: 154 | 155 | # ignore if function name contains "__" 156 | # like __init__ etc 157 | if "__" not in middle.string: 158 | 159 | # write into dictionary 160 | self._get_new_name(middle.string) 161 | 162 | def _spot_isolated_names(self, token_triple: iter) -> None: 163 | """[summary] 164 | Looks for isolated variable definitions 165 | 166 | [description] 167 | Takes a consecutive token triple and checks if the middle one 168 | if is: 169 | 170 | NAME = ... 171 | 172 | 173 | Arguments: 174 | token_triple {iter} -- consecutive token triple 175 | """ 176 | # get tokens 177 | first, middle, last = token_triple 178 | 179 | # check if "first" empty 180 | if isemptytype(first.type) and middle.type == NAME: 181 | # check if middle is not a build-in name. 182 | # For cases like: 183 | # return ...all 184 | 185 | # also we want to avoid selecting names of isolated function calls. 186 | # Things like: 187 | # function(...) 188 | if not isbuildin(middle.string) and last.string != "(": 189 | # write into dictionary 190 | self._get_new_name(middle.string) 191 | 192 | def _spot_argument_names(self, token_triple: iter) -> None: 193 | """[summary] 194 | Looks for argument names. 195 | 196 | [description] 197 | Takes a consecutive token triple and checks if the middle one 198 | if is: 199 | 200 | def function(NAME1, NAME2 = ..., *NAME3, **NAME4): 201 | 202 | Arguments: 203 | token_triple {iter} -- consecutive token triple 204 | """ 205 | 206 | # get tokens 207 | first, middle, last = token_triple 208 | 209 | # check if we are defining something and if yes, make sure 210 | # the middle is followed by either a comma, = or closing bracket 211 | if "def" in first.line and last.string in ["=", ",", ")", ":"]: 212 | 213 | # also make sure middle is an actual NAME and not "self". 214 | if middle.type == NAME and middle.string != "self": 215 | 216 | # additinally we need to check that it is not an annotation :) 217 | # sorry about the chain of if statements. How to avoid them? 218 | if first.string not in [":", "->"]: 219 | 220 | # write into dictionary 221 | self._get_new_name(middle.string) 222 | 223 | def _substitute(self, tokens): 224 | 225 | result = [] 226 | 227 | # iterating over tokens 228 | for token_type, token_val, _, _, _, in tokens: 229 | 230 | # if token is a Name, substitute from dict. 231 | if token_type == NAME: 232 | 233 | try: 234 | 235 | # try to get name 236 | new_name = self.dict[token_val] 237 | result.append((NAME, new_name)) 238 | 239 | except KeyError: 240 | 241 | # if name not collected 242 | # leave it as is. 243 | result.append((NAME, token_val)) 244 | 245 | # if not a name, append as is. 246 | else: 247 | result.append((token_type, token_val)) 248 | 249 | return result 250 | 251 | @fix_wrapper 252 | def fix(self, tokens): 253 | 254 | tokens, tokens_copy = tee(tokens) 255 | 256 | # iterate over consecutive token triples 257 | for win in window(tokens, 3): 258 | 259 | # collect names in each 260 | self._spot_definitions(win) 261 | self._spot_argument_names(win) 262 | self._spot_isolated_names(win) 263 | 264 | # after spotting all, substitute all names according 265 | # to our collected dictionary 266 | result = self._substitute(tokens_copy) 267 | 268 | return result 269 | -------------------------------------------------------------------------------- /src/lancer/resources/lyrics.txt: -------------------------------------------------------------------------------- 1 | DJ Tim Johnson, I want everybody to stop what they're doin' (Mr. 305) 2 | Now if you know you're with somebody 3 | You're gonna take the hotel room tonight, Make some noise (woo) 4 | Meet me at the hotel room, Meet me at the hotel room 5 | Meet me at the hotel room, Meet me at the hotel room 6 | Forget about your boyfriend And meet me at the hotel room 7 | You can bring your girlfriends And meet me at the hotel room 8 | Forget about your boyfriend And meet me at the hotel room 9 | You can bring your girlfriends And meet me at the hotel room. 10 | We at the hotel, motel, Holiday Inn, We at the hotel, motel, Holiday Inn. 11 | We at the hotel, motel, Holiday Inn, We at the hotel, motel, Holiday Inn. 12 | She like that freaky stuff, Two in the "O" and one in the eye 13 | That kinky stuff, you nasty, But I like your type 14 | And like T.I., it's whatever you like, Bring your girls, just whatever the night 15 | Your man just left, I'm the plumber tonight, 16 | I'll check yo' pipes, oh you the healthy type, Well, here goes some egg whites. 17 | Now gimme that sweet, That nasty, that Gucci stuff 18 | Let me tell you what we gon' do, Two plus two, I'm gon' undress you. 19 | Then we're gonna go three and three, You gon' undress me. 20 | Then we're gonna go four and four, We gon' freak some more, but first 21 | Forget about your boyfriend And meet me at the hotel room 22 | You can bring your girlfriends And meet me at the hotel room 23 | Forget about your boyfriend And meet me at the hotel room 24 | You can bring your girlfriends And meet me at the hotel room 25 | We at the hotel, motel, Holiday Inn, We at the hotel, motel, Holiday Inn. 26 | We at the hotel, motel, Holiday Inn, We at the hotel, motel, Holiday Inn. 27 | After party in hotel lobby, Then we off to the room like vroom 28 | Put them fingers in yo' mouth, or open up yo' blouse, And pull that g-string down south 29 | Ooh, okay shawty, one's company, Two's a crowd and three's a party 30 | Your girl ain't with it, I got somebody, In my nature, she's naughty. 31 | Now gimme that sweet, That nasty, that Gucci stuff 32 | Let me tell you what we gon' do, Two plus two, I'm gon' undress you. 33 | Then we're gonna go three and three, You gon' undress me. 34 | Then we're gonna go four and four, We gon' freak some more, but first 35 | Forget about your boyfriend And meet me at the hotel room 36 | You can bring your girlfriends And meet me at the hotel room 37 | Forget about your boyfriend And meet me at the hotel room 38 | You can bring your girlfriends And meet me at the hotel room 39 | We at the hotel, motel, Holiday Inn, We at the hotel, motel, Holiday Inn. 40 | We at the hotel, motel, Holiday Inn, We at the hotel, motel, Holiday Inn. 41 | Mujeres!... ey-oh-ey-oh-ey... Yenny... oye, que bola? 42 | Mujeres!... ey-oh-ey-oh-ey... Yaminel... oye, que bola? 43 | Mujeres!... ey-oh-ey-oh-ey... Roslyn... oye, que bola? 44 | Mujeres!... ey-oh-ey-oh-ey... Zuleyma... oye, que bola? 45 | Mujeres!... ey-oh-ey-oh-ey... Cristina... oye, que bola? 46 | Mujeres!... ey-oh-ey-oh-ey... Catolina... oye, que bola? 47 | Mujeres!... ey-oh-ey-oh-ey... Sophia... oye, que bola? 48 | Mujeres!... ay-oh-ay-oh-ay... Estefany... oye, que bola? Dale 49 | I don't play football but I've touched down everywhere 50 | (everywhere) everywhere 51 | I don't play baseball but I've hit a home run everywhere, everywhere 52 | I've been to countries and cities I can't pronounce 53 | And the places on the globe I didn't know existed 54 | In Romania she pulled me to the centre 55 | she said Pit you can have me and my sister 56 | In Lebanon yeah the women are bomb 57 | And in Greece you've guessed it the women are sweet 58 | Spinned all around the world but I ain't gon' lie 59 | there's nothing like Miami's heat 60 | Welcome to Miami where them boys used to touch tourists on a daily basis 61 | Duck charges therefore hardly caught cases 62 | This is a city full of culture and different races 63 | Where all the mami's come fully equipped at young ages 64 | With the hurricanes cause even the biggest hurricane couldn't phase us 65 | We got a dome for the Heat that put y'all to sleep 66 | and we carry hits from night till morning 67 | Like Marino strong armin' the game 68 | but I'm not retiring till I got a championship ring 69 | poppin champagne simple and plain 70 | check the map and look where we at 71 | the bottom, simple as that 72 | I'm bringing it back to the fore-front 73 | like Luke in the 2 Live Crew days 74 | he's the one that's got these mami's going two waysGod bless Uncle Al but knowin him MIA was probably engraved 75 | on the tombstone that stands over his grave to this day! 76 | HEY 77 | So if your visitin our city and your sittin pretty on duce tres 78 | or duce fours watch where you park your whip 79 | cause they will leave it sittin' on bricks awfully quick 80 | GET-GET-GET-GET-GET FREAKY 81 | Mr. Worldwide 82 | Hey baby, givin' it your all when you're dancin' on me 83 | I wanna see if you give me some more 84 | Hey baby, you can be my girl, I can be your man 85 | And we can pump this jam however you want 86 | Hey baby, pump it from the side, bend it upside down 87 | Or we can pump it from the back to the front 88 | I'm loose (I'm loose) 89 | And everybody knows I get off the chain 90 | Babe, it's the truth (It's the truth) 91 | I'm like Inception, I play with your brain 92 | So I don't sleep or snooze (Snooze) 93 | I don't play no games so d-d-don't-don't-don't get it confused, no 94 | 'Cause you will lose, yeah 95 | Now, now pu-pu-pu-pu-pump it up 96 | And back it up, like a Tonka truck, dale! 97 | Gon' set the roof on fire 98 | Mami on fire, pshh, red hot 99 | Bada bing, bada boom 100 | Mr. Worldwide as I step in the room 101 | I'm a hustler, baby, but that you knew 102 | And tonight it's just me and you, Dalé -------------------------------------------------------------------------------- /src/lancer/resources/sfw_lyrics.txt: -------------------------------------------------------------------------------- 1 | On top of spaghetti all covered with cheese 2 | I lost my poor meatball when somebody sneezed 3 | It rolled off the table, it rolled on the floor 4 | And then my poor meatball rolled out of the door 5 | It rolled in the garden and under a bush 6 | And then my poor meatball was nothing but mush 7 | The mush was as tasty as tasty could be 8 | And early next summer it grew to a tree 9 | The tree was all covered with beautiful moss 10 | It grew great big meatballs and tomato sauce 11 | So if you eat spaghetti all covered with cheese 12 | Hold on to your meatball and don’t ever sneeze 13 | There was a little turtle his name was tiny Tim 14 | I put him in the bathtub to see if he could swim 15 | He drank up all the water, he ate up all the soap 16 | Now he’s in the bathtub with a bubble in his throat 17 | Bubble bubble bubble bubble bubble bubble bubble bubble POP! 18 | If all the raindrops were lemon drops and gumdrops 19 | So what a rain that would be 20 | Standing outside with my mouth open wide 21 | Ah, ah, ah, ah, ah, ah, ah, ah, ah, ah 22 | Oh, what a rain that would be 23 | If all the snowflakes were candy bars and milkshakes 24 | Oh, what a snow that would be 25 | If all the sunbeams were bubblegum and ice cream 26 | Oh, what a sun that would be! 27 | Standing outside, with my mouth open wide 28 | I’m bringing home my baby bumble bee 29 | Won’t my Mommy be so proud of me I’m bringing home my baby bumble bee – OUCH!! It stung me!! 30 | I’m squishin’ up my baby bumble bee 31 | Won’t my Mommy be so proud of me I’m squishin’ up my baby bumble bee – EW!! What a mess!! 32 | I’m lickin’ up my baby bumble bee 33 | Won’t my Mommy be so proud of me I’m lickin’ up my baby bumble bee – ICK!! I feel sick!! 34 | I’m throwin’ up my baby bumble bee 35 | Won’t my Mommy be so proud of me I’m throwin’ up my baby bumble bee – OH!! What a mess!! 36 | I’m wipin’ up my baby bumble bee 37 | Won’t my Mommy be so proud of me I’m wipin’ up my baby bumble bee – OOPS!! Mommy’s new towel!! 38 | I’m wringin’ out my baby bumble bee 39 | Won’t my Mommy be so proud of me I’m wringing out my baby bumble bee – Bye-Bye baby bumble bee!! 40 | Where is Thumbkin? Here I am! Here I am! 41 | How are you today, sir? Very well, I thank you 42 | Run away Run away Run away Run away 43 | Where is Pointer? Here I am! Here I am! 44 | Where is Middleman? Here I am! Here I am 45 | Where is Ringman? Here I am! Here I am! 46 | Where is Pinkie? Here I am! Here I am! 47 | Underwear, underwear, send a pair, send a pair, I can wear 48 | For I left mine lying, on a line a drying 49 | And now I need them they’re not there 50 | Underwear, underwear, get a pair, get a pair, anywhere 51 | The bugle’s blowing, I must be going 52 | For I’ve got to get there if I have to go there bare 53 | The cows in the barn go moo moo moo 54 | moo moo moo moo moo moo moo moo moo moo moo moo 55 | All day long the ducks in the pond go quack quack quack 56 | quack quack quack quack quack quack 57 | All day long the pigs in the pen go oink oink oink 58 | oink oink oink oink oink oink oink oink oink oink 59 | The pigs in the barn go oink oink oink 60 | The sheep in the field go baa baa baa 61 | baa baa baa baa baa baa 62 | One, two, three, four, five 63 | Once I caught a fish alive 64 | Six, seven, eight, nine, ten 65 | Then I let it go again 66 | Why did you let it go? 67 | Because it bit my finger so 68 | Which finger did it bite? 69 | The little finger on my right! 70 | Open them, shut them (open and shut fists) 71 | Give a little clap 72 | Lay them in your lap 73 | Creep them, creep them, 74 | Creep them, creep them 75 | Right up to your chin (walk hands up body to chin) 76 | Open wide your little mouth but do not put them in 77 | Roll them, roll them, roll them, roll them just like this 78 | Shake them, shake them 79 | Blow a little kiss! 80 | One, two, buckle my shoe 81 | Three, four, shut the door 82 | Five, six, pick up sticks 83 | Seven, eight, lay them straight 84 | Nine, ten, begin again 85 | Now everybody sing with me 86 | Okay everybody, one more time 87 | Nine, ten, that’s the end 88 | That’s the end I did it 89 | I have ten little fingers 90 | and they all belong to me I can make them do things 91 | Do you want to see? 92 | I can close them up tight 93 | I can open them wide 94 | I can put them together 95 | I can make them hide 96 | I can make them fly high 97 | I can make them go low 98 | I can fold them like this and hold them just so 99 | Hickory dickory dock 100 | The mouse went up the clock 101 | The clock struck one 102 | The mouse went down 103 | Tick tock, tick tock, tick tock, tick tock 104 | Teddy Bear Teddy Bear touch the ground 105 | If you’re an elephant and you know it stomp your feet 106 | If you’re a monkey and you know it jump up and down 107 | If you’re a crocodile and you know it snap your jaws 108 | If you’re a lion and you know it give a roar 109 | Barney is a dinosaur from our imagination 110 | And when he's tall he's what we call a dinosaur sensation 111 | Barney's friends are big and small 112 | They come from lots of places 113 | After school they meet to play 114 | And sing with happy faces 115 | Barney shows us lots of things 116 | Like how to play pretend 117 | ABC's, and 123's 118 | And how to be a friend 119 | Barney comes to play with us 120 | Whenever we may need him 121 | Barney can be your friend too 122 | If you just make-believe him -------------------------------------------------------------------------------- /src/lancer/resources/sounds.txt: -------------------------------------------------------------------------------- 1 | snort 2 | growl 3 | screech 4 | roar 5 | growl 6 | buzz 7 | snarl 8 | moo 9 | woof 10 | chirp 11 | caw 12 | cah 13 | quack 14 | honk 15 | neigh 16 | hoot 17 | oink 18 | meow 19 | bark 20 | baa 21 | cockadoodledo 22 | gobble 23 | ribbit 24 | roar 25 | grrr -------------------------------------------------------------------------------- /src/lancer/utils.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | from functools import wraps 3 | from pathlib import Path 4 | from tokenize import tokenize, untokenize, DEDENT, INDENT, NEWLINE, NL 5 | import sys 6 | import logging 7 | from collections import deque 8 | from keyword import iskeyword 9 | from shutil import copy2 as copy 10 | 11 | 12 | __author__ = "Levi Borodenko" 13 | __copyright__ = "Levi Borodenko" 14 | __license__ = "mit" 15 | 16 | _logger = logging.getLogger(__name__) 17 | 18 | 19 | def setup_logging(loglevel): 20 | """Setup basic logging 21 | 22 | Args: 23 | loglevel (int): minimum loglevel for emitting messages 24 | """ 25 | logformat = "[%(asctime)s] %(levelname)s:%(name)s:%(message)s" 26 | logging.basicConfig(level=loglevel, stream=sys.stdout, 27 | format=logformat, datefmt="%Y-%m-%d %H:%M:%S") 28 | 29 | 30 | # setting up logger format and default log level 31 | setup_logging(logging.INFO) 32 | 33 | 34 | def fix_wrapper(fix_method): 35 | """Wraps a method that fixes the tokens, so that we can pass a 36 | file to it and it will fix the tokens and create a fixed file. 37 | 38 | [description] 39 | 40 | TODO: 41 | - Add attribute to self pointing to output 42 | Arguments: 43 | fix_funct {method} -- the method that takes tokens and returns 44 | fixed tokens. 45 | """ 46 | @wraps(fix_method) 47 | def wrapper(self=None, in_file: Path="./file.py"): 48 | 49 | # File to be fixed 50 | path = Path(in_file) 51 | self.FILE_PATH = Path(in_file) 52 | 53 | # Saving file name 54 | self.FILE_NAME = Path(in_file).stem 55 | 56 | # check if input file is a python file or lanced python file 57 | if self.FILE_PATH.suffix not in [".py", ".lanced"]: 58 | raise ValueError("File needs to be .py or .lanced") 59 | 60 | # (temporary) output file 61 | out_file = (path.parent / path.name).with_suffix(".lanced") 62 | 63 | with open(in_file, "r") as file: 64 | 65 | # get file content as string 66 | in_file_str = file.read() 67 | 68 | # convert to callable object for tokenize 69 | in_file_bytes = BytesIO(in_file_str.encode('utf-8')).readline 70 | 71 | # Tokenize the script 72 | tokens = tokenize(in_file_bytes) 73 | 74 | # pass tokens to the method that we wrap 75 | result_tokens = fix_method(self, tokens) 76 | 77 | # converting back to string 78 | result = untokenize(result_tokens).decode('utf-8') 79 | 80 | # print resulting script to out_file 81 | with open(out_file, "w+") as file: 82 | print(result, file=file, end="") 83 | 84 | # add pointer to outfile to self. 85 | self.__output__ = out_file 86 | 87 | return wrapper 88 | 89 | 90 | def window(seq: iter, n: int=2): 91 | """[summary] 92 | 93 | [description] 94 | 95 | Arguments: 96 | seq {iter} -- Sequence to iterate over 97 | 98 | Keyword Arguments: 99 | n {int} -- Size of sliding window (default: {2}) 100 | 101 | Yields: 102 | [type] -- [description] 103 | """ 104 | it = iter(seq) 105 | win = deque((next(it, None) for _ in range(n)), maxlen=n) 106 | yield list(win) 107 | append = win.append 108 | for e in it: 109 | append(e) 110 | yield list(win) 111 | 112 | 113 | def isbuildin(name: str) -> bool: 114 | """[summary] 115 | Checks if name is a keyword or build-in function 116 | [description] 117 | 118 | Arguments: 119 | name {str} -- name to be checked 120 | 121 | Returns: 122 | bool -- true if it is a build-in 123 | """ 124 | 125 | blacklist = ["abs", "delattr", "hash", "memoryview", "set", "all", "dict", 126 | "help", "min", "setattr", "any", "dir", "hex", "next", 127 | "slice", "ascii", "divmod", "id", "object", "sorted", 128 | "bin", "enumerate", "input", "oct", "staticmethod", 129 | "bool", "eval", "int", "open", "str", "breakpoint", "exec", 130 | "isinstance", "ord", "sum", "bytearray", "filter", 131 | "issubclass", "pow", "super", "bytes", "float", "iter", 132 | "print", "tuple", "callable", "format", "len", 133 | "property", "type", "chr", "frozenset", "list", 134 | "range", "vars", "classmethod", "getattr", "locals", 135 | "repr", "zip", "compile", "globals", "map", "reversed", 136 | "__import__", "complex", "hasattr", "max", "round", "self"] 137 | 138 | if name in blacklist or iskeyword(name): 139 | return True 140 | else: 141 | return False 142 | 143 | def isemptytype(type: int) -> bool: 144 | """ 145 | Check whether type is an 'empty' token type. 146 | 147 | Arguments: 148 | type {int} -- The token type 149 | 150 | Returns: 151 | bool -- True if type is an 'empty' type, False otherwise. 152 | """ 153 | return type in [NL, NEWLINE, INDENT, DEDENT] 154 | 155 | def copy_and_delete(in_file:Path, out_file:Path): 156 | """[summary] 157 | Copies the contents of in_file into out_file 158 | then deletes in_file if it is different from out_file. 159 | 160 | Arguments: 161 | in_file {Path} -- File to be copied from and then deleted 162 | out_file {Path} -- File to create and copy in_file into. 163 | """ 164 | 165 | # turn to Path instances if not already. 166 | in_file, out_file = Path(in_file), Path(out_file) 167 | 168 | # copy in_file into out_file 169 | copy(in_file, out_file) 170 | 171 | # delete in_file if it is not the same as outfile 172 | if in_file != out_file: 173 | in_file.unlink() 174 | _logger.debug(f"Overwriting {in_file} with {out_file}. Good luck.") 175 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Dummy conftest.py for lancer. 4 | 5 | If you don't know what this is for, just leave it empty. 6 | Read more about conftest.py under: 7 | https://pytest.org/latest/plugins.html 8 | """ 9 | 10 | # import pytest 11 | -------------------------------------------------------------------------------- /tests/test_CommentFixer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | from lancer.fixers.comments import CommentFixer 5 | from pathlib import Path 6 | 7 | __author__ = "Levi Borodenko" 8 | __copyright__ = "Levi Borodenko" 9 | __license__ = "mit" 10 | 11 | 12 | class TestCommentFixer(object): 13 | """Testing the CommentFixer class.""" 14 | 15 | # test file content 16 | TEST_FILE_CONTENT = """ 17 | from pathlib import Path 18 | 19 | 20 | def some_function(some_arg: Path= "lol"): 21 | 22 | a = "b" 23 | 24 | # comment 1 25 | return a 26 | 27 | # comment 2 28 | 29 | 30 | if __name__ == '__main__': 31 | 32 | # comment 3 33 | some_function() 34 | """ 35 | 36 | # test instance 37 | fixer = CommentFixer() 38 | 39 | def test_init(self): 40 | 41 | assert self.fixer.__name__ == "CommentFixer" 42 | 43 | def test_fix(self, tmp_path): 44 | """Testing basic attributes. 45 | """ 46 | # create temporary folder and script file 47 | path = tmp_path / "folder" 48 | path.mkdir() 49 | 50 | file = path / "comments.py" 51 | file.write_text(self.TEST_FILE_CONTENT) 52 | 53 | self.fixer.fix(file) 54 | 55 | assert self.fixer.__output__ == path / "comments.lanced" 56 | 57 | assert isinstance(self.fixer.FILE_PATH, Path) 58 | assert self.fixer.FILE_PATH.name == "comments.py" 59 | 60 | with pytest.raises(ValueError): 61 | self.fixer.fix("Not_a_python_file.txt") 62 | 63 | with pytest.raises(FileNotFoundError): 64 | self.fixer.fix("No_such_file.py") 65 | 66 | def test_get_lyric(self): 67 | """Test random lyric generation. 68 | """ 69 | 70 | lyric = self.fixer._get_lyric() 71 | 72 | assert isinstance(lyric, str) 73 | assert len(lyric) > 2 74 | 75 | another_lyric = self.fixer._get_lyric() 76 | 77 | assert lyric != another_lyric 78 | 79 | # make sure comment starts with "# " 80 | assert lyric[:2] == "# " 81 | 82 | assert self.fixer.NUM_LYRICS > 100 83 | 84 | def test_get_sfw_lyric(self): 85 | """Test random sfw lyric generation. 86 | """ 87 | self.fixer.sfw = True 88 | lyric = self.fixer._get_lyric() 89 | 90 | assert isinstance(lyric, str) 91 | assert len(lyric) > 2 92 | 93 | another_lyric = self.fixer._get_lyric() 94 | 95 | assert lyric != another_lyric 96 | 97 | # make sure comment starts with "# " 98 | assert lyric[:2] == "# " 99 | 100 | assert self.fixer.NUM_LYRICS > 100 101 | -------------------------------------------------------------------------------- /tests/test_VariableFixer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from lancer.fixers.variables import VariableFixer 4 | 5 | __author__ = "Levi Borodenko" 6 | __copyright__ = "Levi Borodenko" 7 | __license__ = "mit" 8 | 9 | 10 | class TestCommentFixer(object): 11 | """Testing the CommentFixer class.""" 12 | 13 | # test file content 14 | TEST_FILE_CONTENT = """ 15 | from pathlib import Path 16 | 17 | 18 | def some_function(some_arg: Path= "lol"): 19 | 20 | a = "b" 21 | 22 | # comment 1 23 | return a 24 | 25 | # comment 2 26 | 27 | 28 | if __name__ == '__main__': 29 | 30 | # comment 3 31 | some_function() 32 | """ 33 | 34 | # test instance 35 | fixer = VariableFixer() 36 | 37 | def test_init(self): 38 | 39 | assert self.fixer.__name__ == "VariableFixer" 40 | 41 | def test_name_gen(self): 42 | 43 | name = self.fixer._get_new_name("TEST") 44 | 45 | assert isinstance(name, str) 46 | 47 | # check that we have saved the old name and the translation 48 | assert self.fixer.dict["TEST"] == name 49 | 50 | # check if same name generates same output 51 | assert name == self.fixer._get_new_name("TEST") 52 | 53 | def test_fix(self, tmp_path): 54 | """Testing it can fix without breaking. 55 | """ 56 | # create temporary folder and script file 57 | path = tmp_path / "folder" 58 | path.mkdir() 59 | 60 | file = path / "variables.py" 61 | file.write_text(self.TEST_FILE_CONTENT) 62 | 63 | self.fixer.fix(file) 64 | --------------------------------------------------------------------------------