├── {{cookiecutter.repo_name}} ├── requirements │ ├── requirements.txt │ ├── requirements-build.txt │ ├── requirements-test.txt │ ├── requirements-dev.txt │ ├── requirements-docs.txt │ └── README.md ├── MANIFEST.in ├── test │ ├── refdata │ │ └── README.md │ └── conftest.py ├── .markdownlint.jsonc ├── refdata │ └── README.md ├── .gitattributes ├── bin │ └── README.md ├── src │ ├── README.md │ └── my_nb_color.py ├── .editorconfig ├── pyproject.toml ├── tox.ini ├── .pre-commit-config.yaml ├── README.md ├── .gitignore ├── notebooks │ ├── my_nb_path.py │ └── skeleton.ipynb ├── setup.py ├── LICENSE └── .gitleaks.toml ├── .gitattributes ├── CODE_OF_CONDUCT.md ├── cookiecutter.json ├── .github └── pull_request_template.md ├── LICENSE ├── hooks └── post_gen_project.py ├── .gitignore ├── CONTRIBUTING.md └── README.md /{{cookiecutter.repo_name}}/requirements/requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include src/{{cookiecutter.package_name}}/py.typed 2 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/requirements/requirements-build.txt: -------------------------------------------------------------------------------- 1 | build 2 | wheel 3 | setupext-janitor 4 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/requirements/requirements-test.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pytest-cov 3 | pytest-sphinx 4 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/test/refdata/README.md: -------------------------------------------------------------------------------- 1 | # Reference pytest datasets 2 | 3 | This folder holds datasets that are used for pytest, and are versioned. 4 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.markdownlint.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "MD041": false, 4 | "MD013": { 5 | "line_length": 100, 6 | "code_blocks": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/refdata/README.md: -------------------------------------------------------------------------------- 1 | # Reference datasets 2 | 3 | This folder holds datasets that are versioned. Typically, these are small, dummy 4 | datasets for dev/test from your favourite CLI, IDE, or debugger. 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Declare files that will always have CRLF line endings on checkout. 5 | *.sln text eol=crlf 6 | *.bat text eol=crlf 7 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Declare files that will always have CRLF line endings on checkout. 5 | *.sln text eol=crlf 6 | *.bat text eol=crlf 7 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/bin/README.md: -------------------------------------------------------------------------------- 1 | # CLI scripts 2 | 3 | This folder holds command line interface (CLI) scripts. This scripts typically provide entry points to kick off common tasks in your data science project, such as model training or inference. 4 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/README.md: -------------------------------------------------------------------------------- 1 | # Source Codes 2 | 3 | This is where you can put your: 4 | 5 | - your module directory that'll get installed by your `setup.py`. 6 | - Amazon SageMaker source dirs. 7 | - additional modules, e.g., for your notebook files under `notebooks/`. 8 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/requirements/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # Recommended additional modules for your dev venv 2 | ipykernel 3 | pre-commit 4 | ruff 5 | black 6 | mypy 7 | types-PyYAML 8 | types-tqdm 9 | types-setuptools 10 | ipdb 11 | ipywidgets 12 | rich 13 | sphinx-autobuild 14 | bump2version 15 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_name": "My new project", 3 | "repo_name": "my-repo", 4 | "author_name": "Your name (or your organization/company/team)", 5 | "description": "A short description of the project.", 6 | "open_source_license": [ 7 | "No license file", 8 | "MIT License", 9 | "Apache-2.0 License" 10 | ], 11 | "package_name": "", 12 | "pre_commit": [ 13 | "foundation", 14 | "advanced" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.patch] 14 | trim_trailing_whitespace = false 15 | 16 | [*.bat] 17 | indent_style = tab 18 | end_of_line = crlf 19 | 20 | # SageMaker's .manifest are .json or .jsonl 21 | [*.{yaml,yml,json,manifest}] 22 | indent_size = 2 23 | 24 | [LICENSE] 25 | insert_final_newline = false 26 | 27 | [Makefile] 28 | indent_style = tab 29 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/test/conftest.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pytest 4 | 5 | 6 | @pytest.fixture(scope="session", autouse=True) 7 | def root_dir(request): 8 | return Path(request.config.rootdir) 9 | 10 | 11 | class Helpers: 12 | @staticmethod 13 | def import_from_file(name, fname): 14 | import importlib.util 15 | 16 | spec = importlib.util.spec_from_file_location(name, fname) 17 | mod = importlib.util.module_from_spec(spec) 18 | spec.loader.exec_module(mod) 19 | return mod 20 | 21 | 22 | @pytest.fixture 23 | def helpers(): 24 | return Helpers 25 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/requirements/requirements-docs.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | 3 | # Uncomment if .rst uses 'self' reference. 4 | # See: https://github.com/spatialaudio/nbsphinx/issues/669 5 | # pydata-sphinx-theme!=0.10.1 6 | 7 | sphinx-toggleprompt 8 | sphinx-autodoc-typehints 9 | sphinxemoji 10 | myst-parser 11 | myst-parser[linkify] 12 | sphinxcontrib-mermaid 13 | sphinx-toolbox 14 | 15 | # https://github.com/spatialaudio/nbsphinx/issues/655 16 | # https://github.com/sphinx-gallery/sphinx-gallery/issues/984 17 | sphinx-gallery<0.11.0 18 | 19 | # Below are dependencies to support notebook-sphinx integration 20 | nbsphinx 21 | ipython 22 | ipykernel 23 | ipywidgets 24 | rich 25 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/requirements/README.md: -------------------------------------------------------------------------------- 1 | # Requirements Files 2 | 3 | | File | Description | 4 | | ------------------------ | --------------------------------------------- | 5 | | `requirements.txt` | Mandatory requirement | 6 | | `requirements-test.txt` | Required for unit tests | 7 | | `requirements-docs.txt` | Required to generate the html documentations. | 8 | {%- if cookiecutter.package_name != "" %} 9 | | `requirements-build.txt` | Required to build the wheel. | 10 | {%- endif %} 11 | | `requirements-dev.txt` | Recommended packages during development. | 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | *Testing done:* 6 | 7 | ## Merge Checklist 8 | 9 | *Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If you're 10 | unsure about any of them, don't hesitate to ask. We're here to help! This is simply a reminder of 11 | what we are going to look for before merging your pull request.* 12 | 13 | - [ ] I have read the [CONTRIBUTING](https://github.com/aws-samples/python-data-science-template/blob/main/CONTRIBUTING.md) document. 14 | - [ ] I have updated any necessary documentation, including [README](https://github.com/aws-samples/python-data-science-template/blob/main/README.md) (if appropriate). 15 | 16 | By submitting this pull request, I confirm that my contribution is made under the terms of the 17 | MIT license. 18 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 100 3 | 4 | [tool.mypy] 5 | ignore_missing_imports = true 6 | show_error_codes = true 7 | files = "**/*.py" 8 | exclude = [ 9 | '^docs/.*', 10 | '^build/.*', 11 | '^venv/.*', 12 | '^notebooks/.*', 13 | '^src/my_nb_color.py', 14 | ] 15 | 16 | [tool.bandit] 17 | skips = ["B101"] 18 | 19 | [tool.ruff] 20 | select =["E", "F", "I", "UP", "RUF"] 21 | fixable = ["E", "F", "I", "UP", "RUF"] 22 | line-length = 100 23 | fix = true 24 | target-version = "py38" 25 | src = ["src"] 26 | ignore-init-module-imports = true 27 | ignore = ["E722", "RUF100"] 28 | extend-exclude = ["examples", "refdata", "test/refdata/"] 29 | 30 | [tool.ruff.per-file-ignores] 31 | "__init__.py" = ["F401", "RUF100"] 32 | 33 | [tool.ruff.isort] 34 | known-first-party = ["train", "inference"] 35 | extra-standard-library = ["smepu"] # Best effort to approximate tox.ini:[isort] 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /hooks/post_gen_project.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | 4 | # missing_ok available only since python-3.8 5 | # https://docs.python.org/3/library/pathlib.html#pathlib.Path.unlink 6 | if sys.version_info[0:2] >= (3, 8): 7 | 8 | def rm(s: Path) -> None: 9 | Path(s).unlink(missing_ok=True) 10 | 11 | else: 12 | 13 | def rm(s: Path) -> None: 14 | try: 15 | Path(s).unlink() 16 | except FileNotFoundError: 17 | pass 18 | 19 | 20 | package_name = "{{cookiecutter.package_name}}".strip() 21 | if package_name == "": 22 | # This project does not contain a Python package, hence, remove setup.py. 23 | rm("setup.py") 24 | rm("MANIFEST.in") 25 | rm("requirements/requirements-build.txt") 26 | else: 27 | pkg_dir = Path("src") / package_name 28 | pkg_dir.mkdir() 29 | (pkg_dir / "__init__.py").touch() 30 | (pkg_dir / "py.typed").touch() 31 | 32 | license = "{{cookiecutter.open_source_license}}" 33 | if license == "No license file": 34 | rm("LICENSE") 35 | 36 | # fmt: off 37 | pre_commit = "{{cookiecutter.pre_commit}}" 38 | if pre_commit != "advanced": 39 | rm(".gitleaks.toml") 40 | 41 | # Display next steps. 42 | cwd = Path().resolve() 43 | message = [ 44 | "#" * 80, 45 | "# Congratulations, your project has been initialized!", 46 | "#", 47 | f"# The generated project is located at {cwd}", 48 | "#", 49 | "# Recommended next steps:", 50 | f"# - cd {cwd}", 51 | "# - git init", 52 | "# - pre-commit autoupdate", 53 | "# - pre-commit install", 54 | "# - review README.md", 55 | ] 56 | if license != "No license file": 57 | message.append("# - review LICENSE") 58 | if package_name != "": 59 | message += [ 60 | "# - review and update setup.py, then remove the exception at the start.", 61 | ] 62 | message += [ 63 | "# - git add .", 64 | "# - git commit", 65 | "#" * 80, 66 | ] 67 | print(*message, sep="\n") 68 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/tox.ini: -------------------------------------------------------------------------------- 1 | # Sample usage: 2 | # tox --sitepackages -e reformat,mypy,flake8 3 | # tox --sitepackages -e pydocstyle 4 | # tox --sitepackages -e mypy,flake8,pydocstyle 5 | # 6 | # tox -e reformat,mypy,flake8 7 | # tox -e pydocstyle 8 | # tox -e mypy,flake8,pydocstyle 9 | 10 | [main] 11 | src_dir= 12 | notebooks 13 | src 14 | 15 | [tox] 16 | envlist = reformat,flake8,pydocstyle 17 | {% if cookiecutter.package_name == "" -%} 18 | skipsdist = True 19 | {%- endif %} 20 | 21 | ; [isort] 22 | ; atomic = True 23 | ; profile = black 24 | ; line_length = 100 25 | ; known_smepu=smepu 26 | ; known_first_party=train,inference 27 | ; sections=FUTURE,SMEPU,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER 28 | ; # Add heading before every import section 29 | ; #import_heading_future=Future 30 | ; #import_heading_stdlib=Standard library 31 | ; #import_heading_firstparty=My stuff 32 | ; #import_heading_thirdparty=Third party 33 | ; #import_heading_localfolder=Local folder 34 | 35 | [pytest] 36 | addopts = 37 | # -vrA 38 | --durations=5 39 | --doctest-modules 40 | --doctest-continue-on-failure 41 | --ignore=setup.py 42 | --ignore=docs 43 | --ignore=src/my_nb_color.py 44 | filterwarnings = 45 | # https://github.com/boto/boto3/issues/1968 46 | ignore:Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated 47 | 48 | [coverage:report] 49 | exclude_lines = 50 | pragma: no cover 51 | raise NotImplementedError 52 | omit = 53 | src/a2rl/nbtools.py 54 | # Ignore "_remote_module_non_scriptable.py not found" due to .pyc from PyTorch. 55 | ignore_errors = True 56 | 57 | [doc8] 58 | max-line-length=100 59 | ignore=D001 60 | 61 | [testenv:reformat] 62 | # Used during development (before committing) to format .py files. 63 | deps = 64 | ruff 65 | black 66 | commands = 67 | ruff {[main]src_dir} 68 | black {[main]src_dir} 69 | 70 | [testenv:mypy] 71 | deps = 72 | mypy 73 | commands = 74 | mypy --install-types --non-interactive {[main]src_dir} 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Jupyter notebook's checkpoints 2 | .ipynb_checkpoints 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # MS-Office temporary files 13 | ~$* 14 | 15 | # OSX specific 16 | .DS_Store 17 | 18 | # VIM temporary files 19 | .*.sw[po] 20 | *.sw[po] 21 | 22 | # Python distribution / packaging 23 | MANIFEST 24 | .Python 25 | env/ 26 | build/ 27 | develop-eggs/ 28 | dist/ 29 | downloads/ 30 | eggs/ 31 | .eggs/ 32 | lib/ 33 | lib64/ 34 | parts/ 35 | sdist/ 36 | var/ 37 | wheels/ 38 | *.egg-info/ 39 | .installed.cfg 40 | *.egg 41 | 42 | # PyInstaller 43 | # Usually these files are written by a python script from a template 44 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 45 | *.manifest 46 | *.spec 47 | 48 | # Installer logs 49 | pip-log.txt 50 | pip-delete-this-directory.txt 51 | 52 | # Unit test / coverage reports 53 | htmlcov/ 54 | .tox/ 55 | .coverage 56 | .coverage.* 57 | .cache 58 | nosetests.xml 59 | coverage.xml 60 | *.cover 61 | .hypothesis/ 62 | .pytest_cache/ 63 | 64 | # Translations 65 | *.mo 66 | *.pot 67 | 68 | # Django stuff: 69 | *.log 70 | local_settings.py 71 | db.sqlite3 72 | 73 | # Flask stuffs 74 | instance/ 75 | .webassets-cache 76 | 77 | # Scrappy stuffs 78 | .scrapy 79 | 80 | # pyenv 81 | .python-version 82 | 83 | # celery beat schedule file 84 | celerybeat-schedule 85 | 86 | # SageMath parsed files 87 | *.sage.py 88 | 89 | # Sphinx documentation 90 | docs/_build/ 91 | 92 | # mkdocs documentation 93 | /site 94 | 95 | # Database 96 | *.db 97 | *.rdb 98 | 99 | # PyBuilder 100 | target/ 101 | 102 | # Mypy cache 103 | .mypy_cache/ 104 | 105 | # Environments 106 | .env 107 | .venv 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # IDE 115 | .c9/ 116 | .idea/ 117 | .spyproject 118 | .spyderproject 119 | .ropeproject 120 | .vscode 121 | 122 | # pyspark 123 | derby.log 124 | metastore_db 125 | spark-warehouse 126 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.6.0 4 | hooks: 5 | - id: check-json 6 | exclude: .vscode 7 | - id: check-merge-conflict 8 | - id: check-symlinks 9 | - id: check-toml 10 | - id: check-yaml 11 | - id: debug-statements 12 | - id: detect-aws-credentials 13 | args: [--allow-missing-credentials] 14 | - id: detect-private-key 15 | - id: end-of-file-fixer 16 | - id: trailing-whitespace 17 | # For setup.cfg exclusion, see https://github.com/c4urself/bump2version/issues/58 18 | exclude: '^setup.cfg$|.*\.diff$|.*\.patch$' 19 | - repo: https://github.com/charliermarsh/ruff-pre-commit 20 | rev: "v0.0.235" 21 | hooks: 22 | - id: ruff 23 | - repo: https://github.com/psf/black 24 | rev: 22.12.0 25 | hooks: 26 | - id: black 27 | # files: ^src\/.* 28 | - repo: https://github.com/pre-commit/mirrors-mypy 29 | rev: v0.991 30 | hooks: 31 | - id: mypy 32 | args: [--install-types, --non-interactive] 33 | # files: ^src\/.* 34 | - repo: https://github.com/nbQA-dev/nbQA 35 | rev: 1.6.1 36 | hooks: 37 | - id: nbqa-pyupgrade 38 | args: [--py38-plus] 39 | - id: nbqa-isort 40 | - id: nbqa-black 41 | - repo: https://github.com/PyCQA/doc8 42 | rev: v1.1.1 43 | hooks: 44 | - id: doc8 45 | {%- if cookiecutter.pre_commit == "advanced" %} 46 | - repo: https://github.com/PyCQA/bandit 47 | rev: 1.7.4 48 | hooks: 49 | - id: bandit 50 | args: [-c, pyproject.toml, --recursive, --exclude, "test/*"] 51 | additional_dependencies: ["bandit[toml]"] 52 | 53 | # May need to run `export GOPROXY=direct` prior to `pre-commit install` 54 | - repo: https://github.com/zricethezav/gitleaks 55 | rev: v8.15.3 56 | hooks: 57 | - id: gitleaks 58 | args: [detect, --verbose, --source, .] 59 | exclude: > 60 | (?x)^( 61 | .tox| 62 | .venv 63 | ) 64 | {%- endif %} 65 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/README.md: -------------------------------------------------------------------------------- 1 | # {{cookiecutter.project_name}} 2 | 3 | {{cookiecutter.description}} 4 | 5 | ## Project Structure 6 | 7 | ```text 8 | {{cookiecutter.repo_name}} 9 | |-- bin/ # CLI scripts 10 | |-- notebooks 11 | | |-- *.ipynb # Jupyter notebooks 12 | | `-- my_nb_path.py # Imported by *.ipynb to treat src/ as PYTHONPATH 13 | |-- requirements/ # Dependencies required by this project 14 | |-- src # Python modules developed in this project 15 | {% if cookiecutter.package_name != "" -%} 16 | | |-- {{cookiecutter.package_name}} 17 | {% endif -%} 18 | | `-- my_nb_color.py # Imported by *.ipynb to colorize their outputs 19 | |-- tests/ # Unit tests 20 | {% if cookiecutter.package_name != "" -%} 21 | |-- MANIFEST.in # Required by setup.py 22 | |-- setup.py # To pip install {{cookiecutter.package_name}} 23 | {% endif %} 24 | # Miscellaneous files 25 | |-- .editorconfig # Editor config (for IDE / editor that support this) 26 | |-- .gitattributes # Files that Git must give special treatments 27 | {% if cookiecutter.pre_commit == "advanced" -%} 28 | |-- .gitleaks.toml # Configuration for Gitleaks tool 29 | {% endif -%} 30 | |-- .gitignore # Git ignore list 31 | |-- .pre-commit-config.yaml # Precommit hooks 32 | |-- LICENSE # License 33 | |-- README.md # Template document 34 | |-- pyproject.toml # Settings for select Python toolchains 35 | `-- tox.ini # Settings for select Python toolchains 36 | ``` 37 | 38 | ## Credits 39 | 40 | This project was initialized by {{cookiecutter.author_name}} using: 41 | 42 | ```bash 43 | cookiecutter https://github.com/aws-samples/python-data-science-template 44 | ``` 45 | 46 | ## License 47 | 48 | This library is licensed under the {{cookiecutter.open_source_license}}.{% if cookiecutter.open_source_license != "No license file" %} See the [LICENSE](LICENSE) file.{%- endif %} 49 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.gitignore: -------------------------------------------------------------------------------- 1 | .env.unversioned 2 | 3 | # vscode: retain launch.json for sample invocation 4 | .vscode/* 5 | !.vscode/launch.json 6 | 7 | # Directories for must-not-be-versioned datasets. 8 | /data/ 9 | test/data/ 10 | 11 | # Jupyter notebook's checkpoints 12 | .ipynb_checkpoints 13 | 14 | # Byte-compiled / optimized / DLL files 15 | __pycache__/ 16 | *.py[cod] 17 | *$py.class 18 | 19 | # C extensions 20 | *.so 21 | 22 | # MS-Office temporary files 23 | ~$* 24 | 25 | # OSX specific 26 | .DS_Store 27 | 28 | # VIM temporary files 29 | .*.sw[po] 30 | *.sw[po] 31 | 32 | # Python distribution / packaging 33 | MANIFEST 34 | .Python 35 | env/ 36 | build/ 37 | develop-eggs/ 38 | dist/ 39 | downloads/ 40 | eggs/ 41 | .eggs/ 42 | lib/ 43 | lib64/ 44 | parts/ 45 | sdist/ 46 | var/ 47 | wheels/ 48 | *.egg-info/ 49 | .installed.cfg 50 | *.egg 51 | 52 | # PyInstaller 53 | # Usually these files are written by a python script from a template 54 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 55 | *.manifest 56 | *.spec 57 | 58 | # Installer logs 59 | pip-log.txt 60 | pip-delete-this-directory.txt 61 | 62 | # Unit test / coverage reports 63 | htmlcov/ 64 | .tox/ 65 | .coverage 66 | .coverage.* 67 | .cache 68 | nosetests.xml 69 | coverage.xml 70 | *.cover 71 | .hypothesis/ 72 | .pytest_cache/ 73 | 74 | # Translations 75 | *.mo 76 | *.pot 77 | 78 | # Django stuff: 79 | *.log 80 | local_settings.py 81 | db.sqlite3 82 | 83 | # Flask stuffs 84 | instance/ 85 | .webassets-cache 86 | 87 | # Scrappy stuffs 88 | .scrapy 89 | 90 | # pyenv 91 | .python-version 92 | 93 | # celery beat schedule file 94 | celerybeat-schedule 95 | 96 | # SageMath parsed files 97 | *.sage.py 98 | 99 | # Sphinx documentation 100 | docs/_build/ 101 | 102 | # mkdocs documentation 103 | /site 104 | 105 | # Database 106 | *.db 107 | *.rdb 108 | 109 | # PyBuilder 110 | target/ 111 | 112 | # Mypy cache 113 | .mypy_cache/ 114 | 115 | # Environments 116 | .env 117 | .venv 118 | env/ 119 | venv/ 120 | ENV/ 121 | env.bak/ 122 | venv.bak/ 123 | 124 | # IDE 125 | .c9/ 126 | .idea/ 127 | .spyproject 128 | .spyderproject 129 | .ropeproject 130 | .vscode 131 | 132 | # pyspark 133 | derby.log 134 | metastore_db 135 | spark-warehouse 136 | 137 | # pytorch, tensorflow, tensorboard 138 | *.pt 139 | *.pt[ch] 140 | *.ckpt 141 | *.ckpt.* 142 | *.h5 143 | *.tfevents.* 144 | 145 | # Neuron-SDK >= 2.14 146 | log-neuron-cc.txt 147 | all_metrics.csv 148 | neuronxcc-*/ 149 | 150 | # WanDb temp files or offline artifacts 151 | wandb/ 152 | 153 | # Enroot squashfile 154 | *.sqsh 155 | 156 | # Slurm 157 | slurm-*.out 158 | slurm-*.err 159 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/notebooks/my_nb_path.py: -------------------------------------------------------------------------------- 1 | """Allow notebooks to import custom modules at a few pre-defined places within this project's 2 | git repository. 3 | 4 | When imported, adds ``GITROOT``, ``GITROOT/src``, and ``GITROOT/notebooks`` to `sys.path`. 5 | 6 | Place this file in the same directory as your ``.ipynb`` files. If ``.ipynb`` files are organized 7 | into subfolders, please ensure this file is presented in each subfolder. Example: 8 | 9 | .. code-block:: bash 10 | 11 | GITROOT 12 | |-- .git # Signify this is a git repository 13 | |-- notebooks # Parent folder of Jupyter notebooks 14 | | |-- folder-a 15 | | | |-- my_nb_path.py # Importable by nb-abc.ipynb and nb-xyz.ipynb 16 | | | |-- nb-abc.ipynb 17 | | | `-- nb-xyz.ipynb 18 | | |-- my_nb_path.py # Importable by nb-01.ipynb and nb-02.ipynb 19 | | |-- nb-01.ipynb 20 | | `-- nb-02.ipynb 21 | `-- src 22 | `-- my_custom_module 23 | |-- __init__.py 24 | `-- ... 25 | 26 | Usage by ``.ipynb``: 27 | 28 | >>> # Allow this notebook to import from GITROOT, GITROOT/src, and GITROOT/notebooks. 29 | >>> # This module must be imported before importing any other custom modules under GITROOT. 30 | >>> # The isort directive prevents the statement to be moved around when isort is used. 31 | >>> import my_nb_path # isort: skip 32 | >>> 33 | >>> # Test-drive importing a custom module under GITROOT/src. 34 | >>> import my_custom_module 35 | 36 | Background: we used to rely on ``ipython_config.py`` in the current working directory. However, 37 | IPython 8.0.1+, 7.31.1+ and 5.11+ disable this behavior for security reason as described 38 | [here](https://ipython.readthedocs.io/en/stable/whatsnew/version8.html#ipython-8-0-1-cve-2022-21699). 39 | 40 | So now, each ``.ipynb`` must explicitly modify its own `sys.path` which is what this module offers 41 | as convenience. 42 | """ 43 | import os 44 | import subprocess # nosec: B404 45 | import sys 46 | from pathlib import Path 47 | from typing import Union 48 | 49 | 50 | def sys_path_append(o: Union[str, os.PathLike]) -> None: 51 | posix_path: str = o.as_posix() if isinstance(o, Path) else Path(o).as_posix() 52 | if posix_path not in sys.path: 53 | sys.path.insert(0, posix_path) 54 | 55 | 56 | try: 57 | # Add GIT_ROOT/ and a few other subdirs 58 | _p = subprocess.run( # nosec: B603 B607 59 | ["git", "rev-parse", "--show-toplevel"], 60 | stdout=subprocess.PIPE, 61 | stderr=subprocess.STDOUT, 62 | ) 63 | 64 | if _p.returncode == 0: 65 | _git_root: str = _p.stdout[:-1].decode("utf-8") # Remove trailing '\n' 66 | _git_root_p = Path(_git_root) 67 | 68 | my_sys_paths = [ 69 | _git_root_p, 70 | _git_root_p / "src", 71 | _git_root_p / "notebooks", 72 | ] 73 | for sp in my_sys_paths: 74 | sys_path_append(sp) 75 | except Exception: # nosec: B110 76 | # Not a proper git: no CLI, not a git repo, ... 77 | # So, don't do anything to sys.path 78 | pass 79 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/setup.py: -------------------------------------------------------------------------------- 1 | raise ValueError( 2 | "Baseline setup.py from cookiecutter aws-samples/python-data-science-template. " 3 | "Please review and modify accordingly, then remove this exception" 4 | ) 5 | 6 | import os 7 | from typing import List 8 | 9 | from setuptools import find_packages, setup 10 | 11 | _repo: str = "{{cookiecutter.repo_name}}" 12 | _pkg: str = "{{cookiecutter.package_name}}" 13 | _version = "0.0.1" 14 | 15 | 16 | def read_lines(fname: str) -> List[str]: 17 | """Read the content of a file. 18 | 19 | You may use this to get the content of, for e.g., requirements.txt, VERSION, etc. 20 | """ 21 | return open(os.path.join(os.path.dirname(__file__), fname)).readlines() 22 | 23 | 24 | def read_requirements(fname: str) -> List[str]: 25 | lines = [ 26 | flip_git(stripped_line) 27 | for stripped_line in (line.strip() for line in read_lines(fname)) 28 | if stripped_line != "" 29 | ] 30 | 31 | return lines 32 | 33 | 34 | def flip_git(s: str) -> str: 35 | """Flip pip's git source from ``requirements.txt`` format to `setuptools` format. 36 | 37 | If `s` is not a git source, return as-is. 38 | 39 | Args: 40 | s (str): a line in ``requirements.txt``. 41 | 42 | Returns: 43 | str: if `s` is ``git+.://gh.com/a/b@c#egg=d``, then return ``d @ git+.://gh.com/a/b@c``. 44 | Otherwise, return as-is. 45 | """ 46 | if not s.startswith("git+"): 47 | return s 48 | git_url, egg = s.rsplit("#", 1) 49 | _, egg_name = egg.split("=") 50 | return f"{egg_name} @ {git_url}" 51 | 52 | 53 | # Declare minimal set for installation 54 | required_packages: List[str] = read_requirements("requirements/requirements.txt") 55 | extras = { 56 | "dev": read_requirements("requirements/requirements-dev.txt"), 57 | "test": read_requirements("requirements/requirements-test.txt"), 58 | } 59 | 60 | setup( 61 | name=_pkg, 62 | packages=find_packages(where="src"), 63 | package_dir={"": "src"}, 64 | include_package_data=True, 65 | version=_version, 66 | description="{{cookiecutter.description}}", 67 | long_description="".join(read_lines("README.md")), 68 | author="{{cookiecutter.author_name}}", 69 | url=f"https://github.com/abcd/{_repo}/", 70 | download_url="", 71 | project_urls={ 72 | "Bug Tracker": f"https://github.com/abcd/{_repo}/issues/", 73 | "Documentation": f"https://{_repo}.readthedocs.io/en/stable/", 74 | "Source Code": f"https://github.com/abcd/{_repo}/", 75 | }, 76 | {% if cookiecutter.open_source_license != "No license file" -%} 77 | license="{{cookiecutter.open_source_license}}", 78 | {% endif -%} 79 | keywords="word1 word2 word3", 80 | platforms=["any"], 81 | classifiers=[ 82 | "Development Status :: 5 - Production/Stable", 83 | "Intended Audience :: Developers", 84 | "Intended Audience :: System Administrators", 85 | "Natural Language :: English", 86 | {% if cookiecutter.open_source_license != "No license file" -%} 87 | "License :: OSI Approved :: {{cookiecutter.open_source_license}}", 88 | {% endif -%} 89 | "Programming Language :: Python", 90 | "Programming Language :: Python :: 3.8", 91 | ], 92 | python_requires=">=3.8.0", 93 | install_requires=required_packages, 94 | extras_require=extras, 95 | ) 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A cookiecutter template for Python data science projects 2 | 3 | Initialize for a modular, Python-based data science project, with opinionated linters configuration. 4 | 5 | **\[20220126\]** If you prefer flake8+isort instead of 6 | [ruff](https://github.com/charliermarsh/ruff), use branch `flake8-isort`. 7 | 8 | ## Pre-requisite 9 | 10 | You need to have the cli `cookiecutter` available in your Python environment. Please see its 11 | installation instructions [here](https://cookiecutter.readthedocs.io/en/latest/installation.html). 12 | 13 | ## Usage 14 | 15 | To generate a directory structure for a new data science project, you can run the following commands 16 | in your Python environment. 17 | 18 | ```bash 19 | cookiecutter https://github.com/aws-samples/python-data-science-template 20 | ``` 21 | 22 | ![setup-example-640px](https://user-images.githubusercontent.com/6405428/130512903-38e4fc96-e5b5-44f5-a4bd-4b4d4c6b3681.gif) 23 | 24 | Alternatively, you can also clone this repository to use a local template: 25 | 26 | ```bash 27 | # Clone to a local repository in the current directory. 28 | git clone https://github.com/aws-samples/python-data-science-template.git 29 | 30 | # The above command creates python-data-science-template/ in the current dir. 31 | 32 | # Use the local repo to generate project structure 33 | cookiecutter python-data-science-template 34 | ``` 35 | 36 | ## Project Structure 37 | 38 | By using this template, your data science project is auto-generated as follows: 39 | 40 | ```text 41 | . 42 | |-- bin/ 43 | |-- notebooks # A directory to place all notebooks files. 44 | | |-- *.ipynb 45 | | `-- my_nb_path.py # Imported by *.ipynb to treat src/ as PYTHONPATH 46 | |-- requirements/ 47 | |-- src 48 | | |-- my_custom_module # Your custom module 49 | | |-- my_nb_color.py # Imported by *.ipynb to colorize their outputs 50 | | `-- source_dir # Additional codes such as SageMaker source dir 51 | |-- tests/ # Unit tests 52 | |-- MANIFEST.in # Required by setup.py (if module name specified) 53 | |-- setup.py # To pip install your Python module (if module name specified) 54 | 55 | # These sample configuration files are auto-generated too: 56 | |-- .editorconfig # Sample editor config (for IDE / editor that supports this) 57 | |-- .gitattributes # Sample .gitattributes 58 | |-- .gitleaks.toml # Sample Gitleaks config (if pre_commit is advanced) 59 | |-- .gitignore # Sample .gitignore 60 | |-- .pre-commit-config.yaml # Sample precommit hooks 61 | |-- LICENSE # Boilperplate (auto-generated) 62 | |-- README.md # Template for you to customize 63 | |-- pyproject.toml # Sample configurations for Python toolchains 64 | `-- tox.ini # Sample configurations for Python toolchains 65 | ``` 66 | 67 | This structure has been used in a few other places as well, e.g., 68 | [aws-samples/sagemaker-rl-energy-storage-system](https://github.com/aws-samples/sagemaker-rl-energy-storage-system) 69 | and 70 | [aws-samples/amazon-sagemaker-gluonts-entrypoint](https://github.com/aws-samples/amazon-sagemaker-gluonts-entrypoint). 71 | Feel free to look at those repositories and observe the project structure documented in their 72 | `README.md`. 73 | 74 | ## Related Projects 75 | 76 | Ready to start your new data science project on AWS? If so, you may want to check on these related 77 | samples. 78 | 79 | 1. Do you like to work on EC2 instances? Then why don't you check out these [simple 80 | template](https://github.com/aws-samples/ec2-data-science-vim-tmux-zsh/) to setup basic Vim, 81 | Tmux, Zsh for the Deep Learning AMI Amazon Linux 2 for data scientsts. 82 | 83 | 2. Do you like to work on SageMaker classic notebook instances? Then why don't you check out the 84 | [one-liner customization 85 | command](https://github.com/aws-samples/amazon-sagemaker-notebook-instance-customization) that 86 | quickly applies common tweaks on a fresh (i.e., newly created or rebooted) SageMaker classic 87 | notebook instance, to make the notebook instance a little bit more ergonomic for prolonged usage. 88 | 89 | 3. Are you loooking for a quickstart to accelerate the delivery of custom ML solutions to 90 | production, without having to make too many design choices? Then why don't you check out the [ML 91 | Max repo](https://github.com/awslabs/mlmax/) which includes templates for four pillars: training 92 | pipeline, inference pipeline, development environment and data management/ETL. 93 | 94 | 4. Are you tired of repeatedly writing the same boilerplate codes for common, tactical data science 95 | tasks? Then why don't you check on the [SageMaker meta-entrypoint 96 | utilities](https://github.com/aws-samples/amazon-sagemaker-entrypoint-utilities), and the 97 | [smallmatter library](https://github.com/aws-samples/smallmatter-package). 98 | 99 | ## Security 100 | 101 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 102 | 103 | ## License 104 | 105 | This library is licensed under the MIT-0 License. See the LICENSE file. 106 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/src/my_nb_color.py: -------------------------------------------------------------------------------- 1 | """Convenience module to setup color prints and logs in a Jupyter notebook. 2 | 3 | Dependencies: `loguru`, `rich`. 4 | 5 | Basic usage by an ``.ipynb``: 6 | 7 | >>> # Colorize notebook outputs 8 | >>> from my_nb_color import print, pprint, oprint, inspect 9 | >>> 10 | >>> # Test-drive different behavior of print functionalities 11 | >>> d = {"A" * 200, "B" * 200} 12 | >>> print("Colored:", d) 13 | >>> pprint("Colored and wrapped:", d) 14 | >>> oprint("Plain (i.e., Python's original):", d) 15 | >>> display(d) 16 | >>> 17 | >>> import pandas as pd 18 | >>> df = pd.DataFrame(dict(a=[1,2,3], b=[4,5,6])) 19 | >>> display(d, df) 20 | >>> df.plot(); 21 | >>> 22 | >>> inspect(df) 23 | >>> inspect(print) 24 | >>> inspect(pprint) 25 | >>> inspect(inspect) 26 | >>> 27 | >>> # Test-drive loguru 28 | >>> from loguru import logger 29 | >>> for f in (logger.debug, logger.info, logger.success, logger.error): 30 | >>> f("Hello World!") 31 | """ 32 | import sys 33 | import warnings 34 | from typing import Callable, cast 35 | 36 | # Try to setup rich. 37 | try: # noqa: C901 38 | import rich 39 | except ModuleNotFoundError: 40 | print = pprint = oprint = print 41 | 42 | def inspect(*args, **kwargs): 43 | warnings.warn(f"{__name__}.inspect() requires rich.") 44 | 45 | else: 46 | from rich.console import Console 47 | 48 | oprint = print # In-case plain old behavior is needed 49 | _console = Console(force_terminal=True, force_jupyter=False) 50 | print = cast(Callable, _console.out) 51 | 52 | def pprint(*args, soft_wrap=True, **kwargs): 53 | """Call ``rich.console.Console(...).print(..., soft_wrap=True, ...)``.""" 54 | _console.print(*args, soft_wrap=soft_wrap, **kwargs) 55 | 56 | class Inspect: 57 | def __init__(self, console=_console): 58 | self.console = _console 59 | 60 | def __call__(self, obj, *args, **kwargs): 61 | """Call ``rich.inspect(..., console=, ...)``.""" 62 | # Do not inspect wrappers, because *args & **kwargs are not useful for callers. 63 | # 64 | # Implementation notes: make sure the pattern is: 65 | # 66 | # if is : 67 | # = 68 | if self is obj: 69 | obj = rich.inspect 70 | elif pprint is obj: 71 | obj = self.console.print 72 | 73 | rich.inspect(obj, *args, console=self.console, **kwargs) 74 | 75 | inspect = cast(Callable, Inspect()) 76 | 77 | def opinionated_rich_pretty_install(): 78 | """Intercept any post-ipython renderings. 79 | 80 | Known cases fixed (as of rich-11.2.0): (i) prevent pandas dataframe rendered twice (as text 81 | and as html), (ii) do not show ``
`` on matplotlib figures. 82 | """ 83 | from IPython import get_ipython 84 | from IPython.core.formatters import BaseFormatter 85 | from rich import get_console 86 | from rich.abc import RichRenderable 87 | from rich.console import ConsoleRenderable 88 | from rich.pretty import Pretty, _safe_isinstance 89 | 90 | class MyRichFormatter(BaseFormatter): 91 | # Based on: rich.pretty.install._ipy_display_hook() 92 | # Customized behaviors are described in the comments. 93 | reprs = [ 94 | "_repr_html_", 95 | "_repr_markdown_", 96 | "_repr_json_", 97 | "_repr_latex_", 98 | "_repr_jpeg_", 99 | "_repr_png_", 100 | "_repr_svg_", 101 | "_repr_mimebundle_", 102 | ] 103 | 104 | def __call__(self, value, *args, **kwargs): 105 | console = get_console() 106 | if console.is_jupyter: 107 | for repr_name in self.reprs: 108 | try: 109 | repr_method = getattr(value, repr_name) 110 | repr_result = repr_method() 111 | except ( 112 | AttributeError, # value object has does not have the repr attribute 113 | Exception, # any other error 114 | ): 115 | continue 116 | else: 117 | # Customized behavior: once rendered by ipython's repr, do no further. 118 | if repr_result is not None: 119 | return 120 | 121 | # Customized behavior: when None of the ipython repr work, output color ascii. 122 | console = Console(force_terminal=True, force_jupyter=False) 123 | # End of customized behavior 124 | 125 | # certain renderables should start on a new line 126 | if _safe_isinstance(value, ConsoleRenderable): 127 | console.line() 128 | 129 | with console.capture() as capture: 130 | console.print( 131 | value 132 | if _safe_isinstance(value, RichRenderable) 133 | else Pretty( 134 | value, 135 | overflow="ignore", 136 | indent_guides=False, 137 | max_length=None, 138 | max_string=None, 139 | expand_all=False, 140 | margin=12, 141 | ), 142 | crop=False, 143 | new_line_start=True, 144 | ) 145 | return capture.get() 146 | 147 | rich.reconfigure(force_terminal=True) 148 | rich.pretty.install() 149 | ipy_formatters = get_ipython().display_formatter.formatters 150 | rich_formatter = ipy_formatters["text/plain"] 151 | if rich_formatter.__module__ == "rich.pretty": 152 | ipy_formatters["text/plain"] = MyRichFormatter() 153 | 154 | opinionated_rich_pretty_install() 155 | 156 | # Try to setup loguru. 157 | try: 158 | from loguru import logger 159 | except ModuleNotFoundError: 160 | pass 161 | else: 162 | logger.configure(handlers=[dict(sink=sys.stderr, colorize=True)]) 163 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/LICENSE: -------------------------------------------------------------------------------- 1 | {% if cookiecutter.open_source_license == "MIT License" -%} 2 | The MIT License (MIT) 3 | Copyright (c) <{% now 'local', '%Y' %}> {{cookiecutter.author_name}}. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | {% elif cookiecutter.open_source_license == "Apache-2.0 License" -%} 21 | Apache License 22 | Version 2.0, January 2004 23 | 24 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 25 | 26 | 1. Definitions. 27 | 28 | "License" shall mean the terms and conditions for use, reproduction, 29 | and distribution as defined by Sections 1 through 9 of this document. 30 | 31 | "Licensor" shall mean the copyright owner or entity authorized by 32 | the copyright owner that is granting the License. 33 | 34 | "Legal Entity" shall mean the union of the acting entity and all 35 | other entities that control, are controlled by, or are under common 36 | control with that entity. For the purposes of this definition, 37 | "control" means (i) the power, direct or indirect, to cause the 38 | direction or management of such entity, whether by contract or 39 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 40 | outstanding shares, or (iii) beneficial ownership of such entity. 41 | 42 | "You" (or "Your") shall mean an individual or Legal Entity 43 | exercising permissions granted by this License. 44 | 45 | "Source" form shall mean the preferred form for making modifications, 46 | including but not limited to software source code, documentation 47 | source, and configuration files. 48 | 49 | "Object" form shall mean any form resulting from mechanical 50 | transformation or translation of a Source form, including but 51 | not limited to compiled object code, generated documentation, 52 | and conversions to other media types. 53 | 54 | "Work" shall mean the work of authorship, whether in Source or 55 | Object form, made available under the License, as indicated by a 56 | copyright notice that is included in or attached to the work 57 | (an example is provided in the Appendix below). 58 | 59 | "Derivative Works" shall mean any work, whether in Source or Object 60 | form, that is based on (or derived from) the Work and for which the 61 | editorial revisions, annotations, elaborations, or other modifications 62 | represent, as a whole, an original work of authorship. For the purposes 63 | of this License, Derivative Works shall not include works that remain 64 | separable from, or merely link (or bind by name) to the interfaces of, 65 | the Work and Derivative Works thereof. 66 | 67 | "Contribution" shall mean any work of authorship, including 68 | the original version of the Work and any modifications or additions 69 | to that Work or Derivative Works thereof, that is intentionally 70 | submitted to Licensor for inclusion in the Work by the copyright owner 71 | or by an individual or Legal Entity authorized to submit on behalf of 72 | the copyright owner. For the purposes of this definition, "submitted" 73 | means any form of electronic, verbal, or written communication sent 74 | to the Licensor or its representatives, including but not limited to 75 | communication on electronic mailing lists, source code control systems, 76 | and issue tracking systems that are managed by, or on behalf of, the 77 | Licensor for the purpose of discussing and improving the Work, but 78 | excluding communication that is conspicuously marked or otherwise 79 | designated in writing by the copyright owner as "Not a Contribution." 80 | 81 | "Contributor" shall mean Licensor and any individual or Legal Entity 82 | on behalf of whom a Contribution has been received by Licensor and 83 | subsequently incorporated within the Work. 84 | 85 | 2. Grant of Copyright License. Subject to the terms and conditions of 86 | this License, each Contributor hereby grants to You a perpetual, 87 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 88 | copyright license to reproduce, prepare Derivative Works of, 89 | publicly display, publicly perform, sublicense, and distribute the 90 | Work and such Derivative Works in Source or Object form. 91 | 92 | 3. Grant of Patent License. Subject to the terms and conditions of 93 | this License, each Contributor hereby grants to You a perpetual, 94 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 95 | (except as stated in this section) patent license to make, have made, 96 | use, offer to sell, sell, import, and otherwise transfer the Work, 97 | where such license applies only to those patent claims licensable 98 | by such Contributor that are necessarily infringed by their 99 | Contribution(s) alone or by combination of their Contribution(s) 100 | with the Work to which such Contribution(s) was submitted. If You 101 | institute patent litigation against any entity (including a 102 | cross-claim or counterclaim in a lawsuit) alleging that the Work 103 | or a Contribution incorporated within the Work constitutes direct 104 | or contributory patent infringement, then any patent licenses 105 | granted to You under this License for that Work shall terminate 106 | as of the date such litigation is filed. 107 | 108 | 4. Redistribution. You may reproduce and distribute copies of the 109 | Work or Derivative Works thereof in any medium, with or without 110 | modifications, and in Source or Object form, provided that You 111 | meet the following conditions: 112 | 113 | (a) You must give any other recipients of the Work or 114 | Derivative Works a copy of this License; and 115 | 116 | (b) You must cause any modified files to carry prominent notices 117 | stating that You changed the files; and 118 | 119 | (c) You must retain, in the Source form of any Derivative Works 120 | that You distribute, all copyright, patent, trademark, and 121 | attribution notices from the Source form of the Work, 122 | excluding those notices that do not pertain to any part of 123 | the Derivative Works; and 124 | 125 | (d) If the Work includes a "NOTICE" text file as part of its 126 | distribution, then any Derivative Works that You distribute must 127 | include a readable copy of the attribution notices contained 128 | within such NOTICE file, excluding those notices that do not 129 | pertain to any part of the Derivative Works, in at least one 130 | of the following places: within a NOTICE text file distributed 131 | as part of the Derivative Works; within the Source form or 132 | documentation, if provided along with the Derivative Works; or, 133 | within a display generated by the Derivative Works, if and 134 | wherever such third-party notices normally appear. The contents 135 | of the NOTICE file are for informational purposes only and 136 | do not modify the License. You may add Your own attribution 137 | notices within Derivative Works that You distribute, alongside 138 | or as an addendum to the NOTICE text from the Work, provided 139 | that such additional attribution notices cannot be construed 140 | as modifying the License. 141 | 142 | You may add Your own copyright statement to Your modifications and 143 | may provide additional or different license terms and conditions 144 | for use, reproduction, or distribution of Your modifications, or 145 | for any such Derivative Works as a whole, provided Your use, 146 | reproduction, and distribution of the Work otherwise complies with 147 | the conditions stated in this License. 148 | 149 | 5. Submission of Contributions. Unless You explicitly state otherwise, 150 | any Contribution intentionally submitted for inclusion in the Work 151 | by You to the Licensor shall be under the terms and conditions of 152 | this License, without any additional terms or conditions. 153 | Notwithstanding the above, nothing herein shall supersede or modify 154 | the terms of any separate license agreement you may have executed 155 | with Licensor regarding such Contributions. 156 | 157 | 6. Trademarks. This License does not grant permission to use the trade 158 | names, trademarks, service marks, or product names of the Licensor, 159 | except as required for reasonable and customary use in describing the 160 | origin of the Work and reproducing the content of the NOTICE file. 161 | 162 | 7. Disclaimer of Warranty. Unless required by applicable law or 163 | agreed to in writing, Licensor provides the Work (and each 164 | Contributor provides its Contributions) on an "AS IS" BASIS, 165 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 166 | implied, including, without limitation, any warranties or conditions 167 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 168 | PARTICULAR PURPOSE. You are solely responsible for determining the 169 | appropriateness of using or redistributing the Work and assume any 170 | risks associated with Your exercise of permissions under this License. 171 | 172 | 8. Limitation of Liability. In no event and under no legal theory, 173 | whether in tort (including negligence), contract, or otherwise, 174 | unless required by applicable law (such as deliberate and grossly 175 | negligent acts) or agreed to in writing, shall any Contributor be 176 | liable to You for damages, including any direct, indirect, special, 177 | incidental, or consequential damages of any character arising as a 178 | result of this License or out of the use or inability to use the 179 | Work (including but not limited to damages for loss of goodwill, 180 | work stoppage, computer failure or malfunction, or any and all 181 | other commercial damages or losses), even if such Contributor 182 | has been advised of the possibility of such damages. 183 | 184 | 9. Accepting Warranty or Additional Liability. While redistributing 185 | the Work or Derivative Works thereof, You may choose to offer, 186 | and charge a fee for, acceptance of support, warranty, indemnity, 187 | or other liability obligations and/or rights consistent with this 188 | License. However, in accepting such obligations, You may act only 189 | on Your own behalf and on Your sole responsibility, not on behalf 190 | of any other Contributor, and only if You agree to indemnify, 191 | defend, and hold each Contributor harmless for any liability 192 | incurred by, or claims asserted against, such Contributor by reason 193 | of your accepting any such warranty or additional liability. 194 | 195 | END OF TERMS AND CONDITIONS 196 | {% endif %} 197 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/notebooks/skeleton.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "
Title

\n", 8 | "\n", 9 | "This skeleton notebook includes reference structure and stanzas, and tips to make notebook as\n", 10 | "ergonomic (for both (co-)authors and readers) and camera-ready as possible.\n", 11 | "\n", 12 | "**NOTE:**\n", 13 | "- Best viewed using Jupyter Lab.\n", 14 | "- The title is a styled sentence rather than `h1`, to prevent it being showed and numbered in TOC.\n", 15 | "\n", 16 | "**NOTE:** this skeleton notebook is meant for reading. To run it,\n", 17 | "please install additional dependencies imported in the second next cell which starts with line\n", 18 | "`# Dependencies required`." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": null, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "%matplotlib inline\n", 28 | "%config InlineBackend.figure_format = 'retina'\n", 29 | "%load_ext autoreload\n", 30 | "%autoreload 2\n", 31 | "\n", 32 | "# Make sure my_nb_path is imported first (and when isort is used, it needs to be told).\n", 33 | "import my_nb_path # isort: skip\n", 34 | "from my_nb_color import print, pprint, inspect" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "# Dependencies required\n", 44 | "import ndpretty\n", 45 | "import numpy as np\n", 46 | "import pandas as pd\n", 47 | "import sagemaker as sm\n", 48 | "from IPython.display import Markdown\n", 49 | "from loguru import logger\n", 50 | "from smallmatter.ds import mask_df # See: https://github.com/aws-samples/smallmatter-package/\n", 51 | "\n", 52 | "# A few standard SageMaker's stanzas. Use type annotation to be verbose.\n", 53 | "role: str = sm.get_execution_role()\n", 54 | "sess = sm.Session()\n", 55 | "region: str = sess.boto_session.region_name" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "# Global setup\n", 63 | "\n", 64 | "This section contains Python variables that should be personalized such as:\n", 65 | "- the name of Amazon S3 bucket and/or prefix may vary from one project member to another.\n", 66 | "- the filename of the dataset to run.\n", 67 | "\n", 68 | "We also show a pattern to automatically synchronize the Python variable to environment variables.\n", 69 | "The idea is to centralized all changes to only this section, then you can safely run the remaining\n", 70 | "cells without having to worry about outdated hardcoded values in the Python, `!`, and `%%` codes.\n", 71 | "\n", 72 | "
Note on heading\n", 73 | "\n", 74 | "> This section starts with an `h1` heading. Thus, it will appears in the TOC as \"*1. Global setup*\".\n", 75 | "

" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "####################################################################################################\n", 85 | "# Change me\n", 86 | "####################################################################################################\n", 87 | "bucket_name = \"my-bucket-name\"\n", 88 | "prefix_name = \"some/prefix\"\n", 89 | "####################################################################################################\n", 90 | "\n", 91 | "\n", 92 | "####################################################################################################\n", 93 | "# Do not change the next lines, as they're derived and will be recomputed automatically.\n", 94 | "####################################################################################################\n", 95 | "s3_prefix = f\"s3://{bucket_name}/{prefix_name}\".rstrip(\"/\")\n", 96 | "\n", 97 | "# Synchronize Python variable and environment variable.\n", 98 | "%set_env S3_PREFIX=$s3_prefix\n", 99 | "%env S3_PREFIX_CELL_SCOPE=$s3_prefix\n", 100 | "\n", 101 | "# Demonstrate the difference between %env and %set_env.\n", 102 | "!echo $S3_PREFIX_CELL_SCOPE # Should print s3://my-bucket-name/some/prefix\n", 103 | "!echo $S3_PREFIX # Should print s3://my-bucket-name/some/prefix" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "metadata": {}, 110 | "outputs": [], 111 | "source": [ 112 | "# Demonstrate the difference between %env and %set_env\n", 113 | "!echo $S3_PREFIX # Should print s3://my-bucket-name/some/prefix\n", 114 | "!echo $S3_PREFIX_CELL_SCOPE # Should print an empty string" 115 | ] 116 | }, 117 | { 118 | "cell_type": "markdown", 119 | "metadata": {}, 120 | "source": [ 121 | "Next cell demonstrates the benefit of having environment variables synchronized to Python variables.\n", 122 | "By avoiding hardcoding, you can parameterized your `!` or `%%` commands, to avoid changing hardcoded\n", 123 | "values scatterred throughout this notebook.\n", 124 | "\n", 125 | "*You can also use a raw cell instead of a markdown cell, however do note raw cells may not be\n", 126 | "rendered correctly outside of Jupyter Lab.*" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "```bash\n", 134 | "####################################################################################################\n", 135 | "# Demonstrate the benefit of avoiding hardcoding as much as possible.\n", 136 | "####################################################################################################\n", 137 | "\n", 138 | "# Whenever `bucket_name` and `prefix_name` are updated (i.e., variables are changed and their cell\n", 139 | "# are executed), the next CLI is guaranteed to always list the updated Amazon S3 prefix.\n", 140 | "!aws s3 ls --recursive $S3_PREFIX/\n", 141 | "\n", 142 | "\n", 143 | "# SEGWAY: since we're talking about aws-cli, here're a few tricks to read Amazon S3 files without\n", 144 | "# having to first download and save those files to your local filesystem.\n", 145 | "\n", 146 | "# Show the first few rows of a file in Amazon S3. NOTE: you can safely ignore the broken pipe error.\n", 147 | "!aws s3 cp $S3_PREFIX/haha.txt - | head \n", 148 | "\n", 149 | "# List files in an archive\n", 150 | "!aws s3 cp $S3_PREFIX/model.tar.gz - | tar -tzvf -\n", 151 | "```" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": [ 158 | "# Improved output" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "## Colored outputs" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "metadata": {}, 172 | "outputs": [ 173 | { 174 | "name": "stdout", 175 | "output_type": "stream", 176 | "text": [ 177 | "Colored: \u001b[1m{\u001b[0m\u001b[32m'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'\u001b[0m, \u001b[32m'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'\u001b[0m\u001b[1m}\u001b[0m\n", 178 | "Colored and wrapped:\n", 179 | "\u001b[1m{\u001b[0m\n", 180 | " \u001b[32m'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u001b[0m\n", 181 | "\u001b[32mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\u001b[0m\n", 182 | "\u001b[32mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'\u001b[0m,\n", 183 | " \u001b[32m'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\u001b[0m\n", 184 | "\u001b[32mBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\u001b[0m\n", 185 | "\u001b[32mBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'\u001b[0m\n", 186 | "\u001b[1m}\u001b[0m\n", 187 | "\n", 188 | "\u001b[1m{\u001b[0m\n", 189 | " \u001b[32m'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'\u001b[0m,\n", 190 | " \u001b[32m'BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB'\u001b[0m\n", 191 | "\u001b[1m}\u001b[0m\n" 192 | ] 193 | }, 194 | { 195 | "name": "stderr", 196 | "output_type": "stream", 197 | "text": [ 198 | "\u001b[32m2022-01-22 17:23:03.529\u001b[0m | \u001b[34m\u001b[1mDEBUG \u001b[0m | \u001b[36m__main__\u001b[0m:\u001b[36m\u001b[0m:\u001b[36m7\u001b[0m - \u001b[34m\u001b[1mHello World!\u001b[0m\n", 199 | "\u001b[32m2022-01-22 17:23:03.530\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36m__main__\u001b[0m:\u001b[36m\u001b[0m:\u001b[36m7\u001b[0m - \u001b[1mHello World!\u001b[0m\n", 200 | "\u001b[32m2022-01-22 17:23:03.531\u001b[0m | \u001b[32m\u001b[1mSUCCESS \u001b[0m | \u001b[36m__main__\u001b[0m:\u001b[36m\u001b[0m:\u001b[36m7\u001b[0m - \u001b[32m\u001b[1mHello World!\u001b[0m\n", 201 | "\u001b[32m2022-01-22 17:23:03.532\u001b[0m | \u001b[31m\u001b[1mERROR \u001b[0m | \u001b[36m__main__\u001b[0m:\u001b[36m\u001b[0m:\u001b[36m7\u001b[0m - \u001b[31m\u001b[1mHello World!\u001b[0m\n" 202 | ] 203 | } 204 | ], 205 | "source": [ 206 | "d = {\"A\" * 200, \"B\" * 200}\n", 207 | "print(\"Colored:\", d)\n", 208 | "pprint(\"Colored and wrapped:\", d)\n", 209 | "display(d)\n", 210 | "\n", 211 | "for f in (logger.debug, logger.info, logger.success, logger.error):\n", 212 | " f(\"Hello World!\")" 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": {}, 218 | "source": [ 219 | "## Dataframes" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": null, 225 | "metadata": {}, 226 | "outputs": [ 227 | { 228 | "data": { 229 | "text/markdown": [ 230 | "### Plain dataframe\n", 231 | "**NOTE:** this also appears in TOC as \"*2.2.1. Plain dataframe*\"" 232 | ], 233 | "text/plain": [ 234 | "" 235 | ] 236 | }, 237 | "metadata": {}, 238 | "output_type": "display_data" 239 | }, 240 | { 241 | "data": { 242 | "text/html": [ 243 | "
\n", 244 | "\n", 257 | "\n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | "
ab
014
125
236
\n", 283 | "
" 284 | ], 285 | "text/plain": [ 286 | " a b\n", 287 | "0 1 4\n", 288 | "1 2 5\n", 289 | "2 3 6" 290 | ] 291 | }, 292 | "metadata": {}, 293 | "output_type": "display_data" 294 | }, 295 | { 296 | "data": { 297 | "text/markdown": [ 298 | "### Masked dataframe\n", 299 | "Sometime, we would like to version the output of this cell into the git repo, to help readers to\n", 300 | "quickly see the shape of a dataframe.\n", 301 | "\n", 302 | "However, when the dataframe contains sensitive values, care must be taken to\n", 303 | "**NEVER** version these values to git.\n", 304 | "Otherwise, as you all know, once checked into the git history, it can be tedious and challenging to\n", 305 | "undo the versioning.\n" 306 | ], 307 | "text/plain": [ 308 | "" 309 | ] 310 | }, 311 | "metadata": {}, 312 | "output_type": "display_data" 313 | }, 314 | { 315 | "data": { 316 | "text/html": [ 317 | "
\n", 318 | "\n", 331 | "\n", 332 | " \n", 333 | " \n", 334 | " \n", 335 | " \n", 336 | " \n", 337 | " \n", 338 | " \n", 339 | " \n", 340 | " \n", 341 | " \n", 342 | " \n", 343 | " \n", 344 | " \n", 345 | " \n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | "
useridpca_apca_b
0xxx0.1-0.30
1xxx0.20.01
2xxx0.30.70
\n", 361 | "
" 362 | ], 363 | "text/plain": [ 364 | " userid pca_a pca_b\n", 365 | "0 xxx 0.1 -0.30\n", 366 | "1 xxx 0.2 0.01\n", 367 | "2 xxx 0.3 0.70" 368 | ] 369 | }, 370 | "metadata": {}, 371 | "output_type": "display_data" 372 | } 373 | ], 374 | "source": [ 375 | "def mask_userid(df: pd.DataFrame) -> pd.DataFrame:\n", 376 | " return mask_df(df, cols=[\"userid\"])\n", 377 | "\n", 378 | "\n", 379 | "df_a = pd.DataFrame(\n", 380 | " {\n", 381 | " \"a\": [1, 2, 3],\n", 382 | " \"b\": [4, 5, 6],\n", 383 | " }\n", 384 | ")\n", 385 | "df_b = pd.DataFrame(\n", 386 | " {\n", 387 | " \"userid\": [1000, 2000, 3000], # Illustration only. Usually read from somewhere.\n", 388 | " \"pca_a\": [0.1, 0.2, 0.3],\n", 389 | " \"pca_b\": [-0.3, 0.01, 0.7],\n", 390 | " }\n", 391 | ")\n", 392 | "\n", 393 | "display(\n", 394 | " Markdown(\n", 395 | " '### Plain dataframe\\n**NOTE:** this also appears in TOC as \"*2.2.1. Plain dataframe*\"'\n", 396 | " ),\n", 397 | " df_a,\n", 398 | " Markdown(\n", 399 | " \"\"\"### Masked dataframe\n", 400 | "Sometime, we would like to version the output of this cell into the git repo, to help readers to\n", 401 | "quickly see the shape of a dataframe.\n", 402 | "\n", 403 | "However, when the dataframe contains sensitive values, care must be taken to\n", 404 | "**NEVER** version these values to git.\n", 405 | "Otherwise, as you all know, once checked into the git history, it can be tedious and challenging to\n", 406 | "undo the versioning.\n", 407 | "\"\"\"\n", 408 | " ),\n", 409 | " mask_userid(df_b),\n", 410 | ")" 411 | ] 412 | }, 413 | { 414 | "cell_type": "markdown", 415 | "metadata": {}, 416 | "source": [ 417 | "## Pretty ndarray display\n", 418 | "\n", 419 | "Example: display a 2D tensor." 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "execution_count": null, 425 | "metadata": {}, 426 | "outputs": [], 427 | "source": [ 428 | "# Affect globally\n", 429 | "ndpretty.default()\n", 430 | "np.random.rand(9, 9)\n", 431 | "\n", 432 | "# NOTE: without ndpretty.default(), use this form:\n", 433 | "# ndpretty.ndarray_html(np.random.rand(3, 4))\n", 434 | "\n", 435 | "# NOTE: the rendered output won't persist." 436 | ] 437 | }, 438 | { 439 | "cell_type": "markdown", 440 | "metadata": {}, 441 | "source": [ 442 | "# Summary\n", 443 | "\n", 444 | "When this notebook should be versioned without output, do a *Clear All Outputs*.\n", 445 | "\n", 446 | "When there're output to be version (like what this skeleton notebook does), consider to remove the\n", 447 | "cell counts.\n", 448 | "\n", 449 | "**Advance git tips:** selectively choose which hunk (i.e., portion) of unstaged change to stage\n", 450 | "using `git add -i filename.ipynb`. For instance, you may edit this notebook on another machine with\n", 451 | "a different Python version or environment name, and these minutae changes need not be committed.\n", 452 | "Please refer to git documentation for more details. TLDR version: choose `5: patch`, then decide\n", 453 | "what to do with each hunk. Best combined with `nbdiff` for a more intuitive diff view.\n", 454 | "\n", 455 | "
Footnote\n", 456 | "\n", 457 | "> This skeleton notebook was ran through the\n", 458 | "> [clr-nb-xcnt.sh](https://github.com/verdimrc/pyutil/blob/master/bin/clr-nb-xcnt.sh) bash script\n", 459 | "> to clear its cell counts.\n", 460 | ">\n", 461 | "> DISCLAIMER: the script is provided on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS\n", 462 | "> OF ANY KIND, either express or implied, including, without limitation, any warranties or\n", 463 | "> conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You\n", 464 | "> are solely responsible for determining the appropriateness of using or redistributing the Work and\n", 465 | "> assume any risks associated with Your exercise of permissions under this Apache License 2.0.\n", 466 | "

" 467 | ] 468 | }, 469 | { 470 | "cell_type": "code", 471 | "execution_count": null, 472 | "metadata": {}, 473 | "outputs": [], 474 | "source": [] 475 | } 476 | ], 477 | "metadata": { 478 | "kernelspec": { 479 | "display_name": "Environment (virtualenv_ds-p310)", 480 | "language": "python", 481 | "name": "virtualenv_ds-p310" 482 | }, 483 | "language_info": { 484 | "codemirror_mode": { 485 | "name": "ipython", 486 | "version": 3 487 | }, 488 | "file_extension": ".py", 489 | "mimetype": "text/x-python", 490 | "name": "python", 491 | "nbconvert_exporter": "python", 492 | "pygments_lexer": "ipython3", 493 | "version": "3.10.2" 494 | }, 495 | "toc-autonumbering": true, 496 | "toc-showcode": false, 497 | "toc-showmarkdowntxt": false, 498 | "toc-showtags": false 499 | }, 500 | "nbformat": 4, 501 | "nbformat_minor": 4 502 | } 503 | -------------------------------------------------------------------------------- /{{cookiecutter.repo_name}}/.gitleaks.toml: -------------------------------------------------------------------------------- 1 | # This is the default gitleaks configuration file. 2 | # Rules and allowlists are defined within this file. 3 | # Rules instruct gitleaks on what should be considered a secret. 4 | # Allowlists instruct gitleaks on what is allowed, i.e. not a secret. 5 | title = "gitleaks config" 6 | 7 | [allowlist] 8 | description = "global allow lists" 9 | regexes = [ 10 | '''219-09-9999''', 11 | '''078-05-1120''', 12 | '''(9[0-9]{2}|666)-\d{2}-\d{4}''', 13 | ] 14 | paths = [ 15 | '''gitleaks.toml''', 16 | '''(.*?)(jpg|gif|doc|pdf|bin|svg|socket)$''', 17 | '''(go.mod|go.sum)$''', 18 | '''^(.tox|.venv)''' 19 | ] 20 | 21 | [[rules]] 22 | description = "Adobe Client ID (Oauth Web)" 23 | id = "adobe-client-id" 24 | regex = '''(?i)(?:adobe)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 25 | secretGroup = 1 26 | keywords = [ 27 | "adobe", 28 | ] 29 | 30 | [[rules]] 31 | description = "Adobe Client Secret" 32 | id = "adobe-client-secret" 33 | regex = '''(?i)\b((p8e-)(?i)[a-z0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 34 | keywords = [ 35 | "p8e-", 36 | ] 37 | 38 | [[rules]] 39 | description = "Age secret key" 40 | id = "age secret key" 41 | regex = '''AGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L]{58}''' 42 | keywords = [ 43 | "age-secret-key-1", 44 | ] 45 | 46 | [[rules]] 47 | description = "Alibaba AccessKey ID" 48 | id = "alibaba-access-key-id" 49 | regex = '''(?i)\b((LTAI)(?i)[a-z0-9]{20})(?:['|\"|\n|\r|\s|\x60]|$)''' 50 | keywords = [ 51 | "ltai", 52 | ] 53 | 54 | [[rules]] 55 | description = "Alibaba Secret Key" 56 | id = "alibaba-secret-key" 57 | regex = '''(?i)(?:alibaba)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{30})(?:['|\"|\n|\r|\s|\x60]|$)''' 58 | secretGroup = 1 59 | keywords = [ 60 | "alibaba", 61 | ] 62 | 63 | [[rules]] 64 | description = "Asana Client ID" 65 | id = "asana-client-id" 66 | regex = '''(?i)(?:asana)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9]{16})(?:['|\"|\n|\r|\s|\x60]|$)''' 67 | secretGroup = 1 68 | keywords = [ 69 | "asana", 70 | ] 71 | 72 | [[rules]] 73 | description = "Asana Client Secret" 74 | id = "asana-client-secret" 75 | regex = '''(?i)(?:asana)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 76 | secretGroup = 1 77 | keywords = [ 78 | "asana", 79 | ] 80 | 81 | [[rules]] 82 | description = "Atlassian API token" 83 | id = "atlassian-api-token" 84 | regex = '''(?i)(?:atlassian|confluence)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{24})(?:['|\"|\n|\r|\s|\x60]|$)''' 85 | secretGroup = 1 86 | keywords = [ 87 | "atlassian","confluence", 88 | ] 89 | 90 | [[rules]] 91 | description = "AWS" 92 | id = "aws-access-token" 93 | regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}''' 94 | keywords = [ 95 | "akia","agpa","aida","aroa","aipa","anpa","anva","asia", 96 | ] 97 | 98 | [[rules]] 99 | description = "BitBucket Client ID" 100 | id = "bitbucket-client-id" 101 | regex = '''(?i)(?:bitbucket)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 102 | secretGroup = 1 103 | keywords = [ 104 | "bitbucket", 105 | ] 106 | 107 | [[rules]] 108 | description = "BitBucket Client Secret" 109 | id = "bitbucket-client-secret" 110 | regex = '''(?i)(?:bitbucket)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{64})(?:['|\"|\n|\r|\s|\x60]|$)''' 111 | secretGroup = 1 112 | keywords = [ 113 | "bitbucket", 114 | ] 115 | 116 | [[rules]] 117 | description = "Beamer API token" 118 | id = "beamer-api-token" 119 | regex = '''(?i)(?:beamer)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(b_[a-z0-9=_\-]{44})(?:['|\"|\n|\r|\s|\x60]|$)''' 120 | secretGroup = 1 121 | keywords = [ 122 | "beamer", 123 | ] 124 | 125 | [[rules]] 126 | description = "Clojars API token" 127 | id = "clojars-api-token" 128 | regex = '''(?i)(CLOJARS_)[a-z0-9]{60}''' 129 | keywords = [ 130 | "clojars", 131 | ] 132 | 133 | [[rules]] 134 | description = "Contentful delivery API token" 135 | id = "contentful-delivery-api-token" 136 | regex = '''(?i)(?:contentful)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{43})(?:['|\"|\n|\r|\s|\x60]|$)''' 137 | secretGroup = 1 138 | keywords = [ 139 | "contentful", 140 | ] 141 | 142 | [[rules]] 143 | description = "Databricks API token" 144 | id = "databricks-api-token" 145 | regex = '''(?i)\b(dapi[a-h0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 146 | keywords = [ 147 | "dapi", 148 | ] 149 | 150 | [[rules]] 151 | description = "Discord API key" 152 | id = "discord-api-token" 153 | regex = '''(?i)(?:discord)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{64})(?:['|\"|\n|\r|\s|\x60]|$)''' 154 | secretGroup = 1 155 | keywords = [ 156 | "discord", 157 | ] 158 | 159 | [[rules]] 160 | description = "Discord client ID" 161 | id = "discord-client-id" 162 | regex = '''(?i)(?:discord)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9]{18})(?:['|\"|\n|\r|\s|\x60]|$)''' 163 | secretGroup = 1 164 | keywords = [ 165 | "discord", 166 | ] 167 | 168 | [[rules]] 169 | description = "Discord client secret" 170 | id = "discord-client-secret" 171 | regex = '''(?i)(?:discord)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 172 | secretGroup = 1 173 | keywords = [ 174 | "discord", 175 | ] 176 | 177 | [[rules]] 178 | description = "Dropbox API secret" 179 | id = "doppler-api-token" 180 | regex = '''(?i)(?:dropbox)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{15})(?:['|\"|\n|\r|\s|\x60]|$)''' 181 | secretGroup = 1 182 | keywords = [ 183 | "dropbox", 184 | ] 185 | 186 | [[rules]] 187 | description = "Dropbox long lived API token" 188 | id = "dropbox-long-lived-api-token" 189 | regex = '''(?i)(?:dropbox)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{11}(AAAAAAAAAA)[a-z0-9\-_=]{43})(?:['|\"|\n|\r|\s|\x60]|$)''' 190 | keywords = [ 191 | "dropbox", 192 | ] 193 | 194 | [[rules]] 195 | description = "Dropbox short lived API token" 196 | id = "dropbox-short-lived-api-token" 197 | regex = '''(?i)(?:dropbox)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(sl\.[a-z0-9\-=_]{135})(?:['|\"|\n|\r|\s|\x60]|$)''' 198 | keywords = [ 199 | "dropbox", 200 | ] 201 | 202 | [[rules]] 203 | description = "Doppler API token" 204 | id = "doppler-api-token" 205 | regex = '''(dp\.pt\.)(?i)[a-z0-9]{43}''' 206 | keywords = [ 207 | "doppler", 208 | ] 209 | 210 | [[rules]] 211 | description = "Duffel API token" 212 | id = "duffel-api-token" 213 | regex = '''duffel_(test|live)_(?i)[a-z0-9_\-=]{43}''' 214 | keywords = [ 215 | "duffel", 216 | ] 217 | 218 | [[rules]] 219 | description = "Dynatrace API token" 220 | id = "dynatrace-api-token" 221 | regex = '''dt0c01\.(?i)[a-z0-9]{24}\.[a-z0-9]{64}''' 222 | keywords = [ 223 | "dynatrace", 224 | ] 225 | 226 | [[rules]] 227 | description = "EasyPost API token" 228 | id = "easypost-api-token" 229 | regex = '''EZAK(?i)[a-z0-9]{54}''' 230 | keywords = [ 231 | "ezak", 232 | ] 233 | 234 | [rules.allowlist] 235 | description = "Ignore .ipynb notebooks" 236 | paths = ['''notebooks/(.*?)(ipynb)$'''] 237 | 238 | [[rules]] 239 | description = "EasyPost test API token" 240 | id = "easypost-test-api-token" 241 | regex = '''EZTK(?i)[a-z0-9]{54}''' 242 | keywords = [ 243 | "eztk", 244 | ] 245 | 246 | [rules.allowlist] 247 | description = "Ignore .ipynb notebooks" 248 | paths = ['''notebooks/(.*?)(ipynb)$'''] 249 | 250 | [[rules]] 251 | description = "facebook" 252 | id = "facebook" 253 | regex = '''(?i)(?:facebook)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 254 | secretGroup = 1 255 | keywords = [ 256 | "facebook", 257 | ] 258 | 259 | [[rules]] 260 | description = "Fastly API key" 261 | id = "fastly-api-token" 262 | regex = '''(?i)(?:fastly)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 263 | secretGroup = 1 264 | keywords = [ 265 | "fastly", 266 | ] 267 | 268 | [[rules]] 269 | description = "Finicity Client Secret" 270 | id = "finicity-client-secret" 271 | regex = '''(?i)(?:finicity)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{20})(?:['|\"|\n|\r|\s|\x60]|$)''' 272 | secretGroup = 1 273 | keywords = [ 274 | "finicity", 275 | ] 276 | 277 | [[rules]] 278 | description = "Finicity API token" 279 | id = "finicity-api-token" 280 | regex = '''(?i)(?:finicity)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 281 | secretGroup = 1 282 | keywords = [ 283 | "finicity", 284 | ] 285 | 286 | [[rules]] 287 | description = "Finicity Public Key" 288 | id = "flutterwave-public-key" 289 | regex = '''FLWPUBK_TEST-(?i)[a-h0-9]{32}-X''' 290 | keywords = [ 291 | "flwpubk_test", 292 | ] 293 | 294 | [[rules]] 295 | description = "Finicity Secret Key" 296 | id = "flutterwave-public-key" 297 | regex = '''FLWSECK_TEST-(?i)[a-h0-9]{32}-X''' 298 | keywords = [ 299 | "flwseck_test", 300 | ] 301 | 302 | [[rules]] 303 | description = "Finicity Secret Key" 304 | id = "flutterwave-public-key" 305 | regex = '''FLWSECK_TEST-(?i)[a-h0-9]{32}-X''' 306 | keywords = [ 307 | "flwseck_test", 308 | ] 309 | 310 | [[rules]] 311 | description = "Frame.io API token" 312 | id = "frameio-api-token" 313 | regex = '''fio-u-(?i)[a-z0-9\-_=]{64}''' 314 | keywords = [ 315 | "fio-u-", 316 | ] 317 | 318 | [[rules]] 319 | description = "GoCardless API token" 320 | id = "gocardless-api-token" 321 | regex = '''(?i)(?:gocardless)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(live_(?i)[a-z0-9\-_=]{40})(?:['|\"|\n|\r|\s|\x60]|$)''' 322 | secretGroup = 1 323 | keywords = [ 324 | "live_","gocardless", 325 | ] 326 | 327 | [[rules]] 328 | description = "GitHub Personal Access Token" 329 | id = "github-pat" 330 | regex = '''ghp_[0-9a-zA-Z]{36}''' 331 | keywords = [ 332 | "ghp_", 333 | ] 334 | 335 | [[rules]] 336 | description = "GitHub OAuth Access Token" 337 | id = "github-oauth" 338 | regex = '''gho_[0-9a-zA-Z]{36}''' 339 | keywords = [ 340 | "gho_", 341 | ] 342 | 343 | [[rules]] 344 | description = "GitHub App Token" 345 | id = "github-app-token" 346 | regex = '''(ghu|ghs)_[0-9a-zA-Z]{36}''' 347 | keywords = [ 348 | "ghu_","ghs_", 349 | ] 350 | 351 | [[rules]] 352 | description = "GitHub Refresh Token" 353 | id = "github-refresh-token" 354 | regex = '''ghr_[0-9a-zA-Z]{36}''' 355 | keywords = [ 356 | "ghr_", 357 | ] 358 | 359 | [[rules]] 360 | description = "Gitlab Personal Access Token" 361 | id = "gitlab-pat" 362 | regex = '''glpat-[0-9a-zA-Z\-\_]{20}''' 363 | keywords = [ 364 | "glpat-", 365 | ] 366 | 367 | [[rules]] 368 | description = "HashiCorp Terraform user/org API token" 369 | id = "hashicorp-tf-api-token" 370 | regex = '''(?i)[a-z0-9]{14}\.atlasv1\.[a-z0-9\-_=]{60,70}''' 371 | keywords = [ 372 | "atlasv1", 373 | ] 374 | 375 | [[rules]] 376 | description = "Heroku API Key" 377 | id = "heroku-api-key" 378 | regex = '''(?i)(?:heroku)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})(?:['|\"|\n|\r|\s|\x60]|$)''' 379 | secretGroup = 1 380 | keywords = [ 381 | "heroku", 382 | ] 383 | 384 | [[rules]] 385 | description = "HubSpot API Token" 386 | id = "hubspot-api-key" 387 | regex = '''(?i)(?:hubspot)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12})(?:['|\"|\n|\r|\s|\x60]|$)''' 388 | secretGroup = 1 389 | keywords = [ 390 | "hubspot", 391 | ] 392 | 393 | [[rules]] 394 | description = "Intercom API Token" 395 | id = "intercom-api-key" 396 | regex = '''(?i)(?:intercom)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9=_\-]{60})(?:['|\"|\n|\r|\s|\x60]|$)''' 397 | secretGroup = 1 398 | keywords = [ 399 | "intercom", 400 | ] 401 | 402 | [[rules]] 403 | description = "Linear API Token" 404 | id = "linear-api-key" 405 | regex = '''lin_api_(?i)[a-z0-9]{40}''' 406 | keywords = [ 407 | "lin_api_", 408 | ] 409 | 410 | [[rules]] 411 | description = "Linear Client Secret" 412 | id = "linear-client-secret" 413 | regex = '''(?i)(?:linear)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 414 | secretGroup = 1 415 | keywords = [ 416 | "linear", 417 | ] 418 | 419 | [[rules]] 420 | description = "LinkedIn Client ID" 421 | id = "linkedin-client-id" 422 | regex = '''(?i)(?:linkedin|linked-in)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{14})(?:['|\"|\n|\r|\s|\x60]|$)''' 423 | secretGroup = 1 424 | keywords = [ 425 | "linkedin","linked-in", 426 | ] 427 | 428 | [[rules]] 429 | description = "LinkedIn Client secret" 430 | id = "linkedin-client-secret" 431 | regex = '''(?i)(?:linkedin|linked-in)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{16})(?:['|\"|\n|\r|\s|\x60]|$)''' 432 | secretGroup = 1 433 | keywords = [ 434 | "linkedin","linked-in", 435 | ] 436 | 437 | [[rules]] 438 | description = "Lob API Key" 439 | id = "lob-api-key" 440 | regex = '''(?i)(?:lob)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}((live|test)_[a-f0-9]{35})(?:['|\"|\n|\r|\s|\x60]|$)''' 441 | secretGroup = 1 442 | keywords = [ 443 | "test_","live_", 444 | ] 445 | 446 | [[rules]] 447 | description = "Lob Publishable API Key" 448 | id = "lob-pub-api-key" 449 | regex = '''(?i)(?:lob)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}((test|live)_pub_[a-f0-9]{31})(?:['|\"|\n|\r|\s|\x60]|$)''' 450 | secretGroup = 1 451 | keywords = [ 452 | "test_pub","live_pub","_pub", 453 | ] 454 | 455 | [[rules]] 456 | description = "Mailchimp API key" 457 | id = "mailchimp-api-key" 458 | regex = '''(?i)(?:mailchimp)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{32}-us20)(?:['|\"|\n|\r|\s|\x60]|$)''' 459 | secretGroup = 1 460 | keywords = [ 461 | "mailchimp", 462 | ] 463 | 464 | [[rules]] 465 | description = "Mailgun public validation key" 466 | id = "mailgun-pub-key" 467 | regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(pubkey-[a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 468 | secretGroup = 1 469 | keywords = [ 470 | "mailgun", 471 | ] 472 | 473 | [[rules]] 474 | description = "Mailgun private API token" 475 | id = "mailgun-private-api-token" 476 | regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(key-[a-f0-9]{32})(?:['|\"|\n|\r|\s|\x60]|$)''' 477 | secretGroup = 1 478 | keywords = [ 479 | "mailgun", 480 | ] 481 | 482 | [[rules]] 483 | description = "Mailgun webhook signing key" 484 | id = "mailgun-signing-key" 485 | regex = '''(?i)(?:mailgun)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-h0-9]{32}-[a-h0-9]{8}-[a-h0-9]{8})(?:['|\"|\n|\r|\s|\x60]|$)''' 486 | secretGroup = 1 487 | keywords = [ 488 | "mailgun", 489 | ] 490 | 491 | [[rules]] 492 | description = "MapBox API token" 493 | id = "mapbox-api-token" 494 | regex = '''(?i)(?:mapbox)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(pk\.[a-z0-9]{60}\.[a-z0-9]{22})(?:['|\"|\n|\r|\s|\x60]|$)''' 495 | secretGroup = 1 496 | keywords = [ 497 | "mapbox", 498 | ] 499 | 500 | [[rules]] 501 | description = "MessageBird API token" 502 | id = "messagebird-api-token" 503 | regex = '''(?i)(?:messagebird|message-bird|message_bird)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{25})(?:['|\"|\n|\r|\s|\x60]|$)''' 504 | secretGroup = 1 505 | keywords = [ 506 | "messagebird","message-bird","message_bird", 507 | ] 508 | 509 | [[rules]] 510 | description = "MessageBird client ID" 511 | id = "messagebird-client-id" 512 | regex = '''(?i)(?:messagebird|message-bird|message_bird)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-h0-9]{8}-[a-h0-9]{4}-[a-h0-9]{4}-[a-h0-9]{4}-[a-h0-9]{12})(?:['|\"|\n|\r|\s|\x60]|$)''' 513 | secretGroup = 1 514 | keywords = [ 515 | "messagebird","message-bird","message_bird", 516 | ] 517 | 518 | [[rules]] 519 | description = "New Relic user API Key" 520 | id = "new-relic-user-api-key" 521 | regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(NRAK-[a-z0-9]{27})(?:['|\"|\n|\r|\s|\x60]|$)''' 522 | secretGroup = 1 523 | keywords = [ 524 | "nrak", 525 | ] 526 | 527 | [[rules]] 528 | description = "New Relic user API ID" 529 | id = "new-relic-user-api-id" 530 | regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{64})(?:['|\"|\n|\r|\s|\x60]|$)''' 531 | secretGroup = 1 532 | keywords = [ 533 | "new-relic","newrelic","new_relic", 534 | ] 535 | 536 | [[rules]] 537 | description = "New Relic ingest browser API token" 538 | id = "new-relic-browser-api-token" 539 | regex = '''(?i)(?:new-relic|newrelic|new_relic)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(NRJS-[a-f0-9]{19})(?:['|\"|\n|\r|\s|\x60]|$)''' 540 | secretGroup = 1 541 | keywords = [ 542 | "nrjs-", 543 | ] 544 | 545 | [[rules]] 546 | description = "npm access token" 547 | id = "npm-access-token" 548 | regex = '''(?i)\b(npm_[a-z0-9]{36})(?:['|\"|\n|\r|\s|\x60]|$)''' 549 | secretGroup = 1 550 | keywords = [ 551 | "npm_", 552 | ] 553 | 554 | [[rules]] 555 | description = "PlanetScale password" 556 | id = "planetscale-password" 557 | regex = '''(?i)\b(pscale_pw_(?i)[a-z0-9=\-_\.]{43})(?:['|\"|\n|\r|\s|\x60]|$)''' 558 | secretGroup = 1 559 | keywords = [ 560 | "pscale_pw_", 561 | ] 562 | 563 | [[rules]] 564 | description = "PlanetScale API token" 565 | id = "planetscale-api-token" 566 | regex = '''(?i)\b(pscale_tkn_(?i)[a-z0-9=\-_\.]{43})(?:['|\"|\n|\r|\s|\x60]|$)''' 567 | secretGroup = 1 568 | keywords = [ 569 | "pscale_tkn_", 570 | ] 571 | 572 | [[rules]] 573 | description = "Postman API token" 574 | id = "postman-api-token" 575 | regex = '''(?i)\b(PMAK-(?i)[a-f0-9]{24}\-[a-f0-9]{34})(?:['|\"|\n|\r|\s|\x60]|$)''' 576 | secretGroup = 1 577 | keywords = [ 578 | "pmak-", 579 | ] 580 | 581 | [[rules]] 582 | description = "Private Key" 583 | id = "private-key" 584 | regex = '''(?i)-----BEGIN[ A-Z0-9_-]{0,100}PRIVATE KEY-----[\s\S-]*KEY----''' 585 | keywords = [ 586 | "-----begin", 587 | ] 588 | 589 | [[rules]] 590 | description = "Pulumi API token" 591 | id = "pulumi-api-token" 592 | regex = '''(?i)\b(pul-[a-f0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)''' 593 | secretGroup = 1 594 | keywords = [ 595 | "pul-", 596 | ] 597 | 598 | [[rules]] 599 | description = "PyPI upload token" 600 | id = "pypi-upload-token" 601 | regex = '''pypi-AgEIcHlwaS5vcmc[A-Za-z0-9\-_]{50,1000}''' 602 | keywords = [ 603 | "pypi-ageichlwas5vcmc", 604 | ] 605 | 606 | [[rules]] 607 | description = "Rubygem API token" 608 | id = "rubygems-api-token" 609 | regex = '''(?i)\b(rubygems_[a-f0-9]{48})(?:['|\"|\n|\r|\s|\x60]|$)''' 610 | secretGroup = 1 611 | keywords = [ 612 | "rubygems_", 613 | ] 614 | 615 | [[rules]] 616 | description = "SendGrid API token" 617 | id = "sendgrid-api-token" 618 | regex = '''(?i)\b(SG\.(?i)[a-z0-9=_\-\.]{66})(?:['|\"|\n|\r|\s|\x60]|$)''' 619 | secretGroup = 1 620 | keywords = [ 621 | "sg.", 622 | ] 623 | 624 | [[rules]] 625 | description = "Sendinblue API token" 626 | id = "sendinblue-api-token" 627 | regex = '''(?i)\b(xkeysib-[a-f0-9]{64}\-(?i)[a-z0-9]{16})(?:['|\"|\n|\r|\s|\x60]|$)''' 628 | secretGroup = 1 629 | keywords = [ 630 | "xkeysib-", 631 | ] 632 | 633 | [[rules]] 634 | description = "Shippo API token" 635 | id = "shippo-api-token" 636 | regex = '''(?i)\b(shippo_(live|test)_[a-f0-9]{40})(?:['|\"|\n|\r|\s|\x60]|$)''' 637 | secretGroup = 1 638 | keywords = [ 639 | "shippo_", 640 | ] 641 | 642 | [[rules]] 643 | description = "Shopify access token" 644 | id = "shopify-access-token" 645 | regex = '''shpat_[a-fA-F0-9]{32}''' 646 | keywords = [ 647 | "shpat_", 648 | ] 649 | 650 | [[rules]] 651 | description = "Shopify custom access token" 652 | id = "shopify-custom-access-token" 653 | regex = '''shpca_[a-fA-F0-9]{32}''' 654 | keywords = [ 655 | "shpca_", 656 | ] 657 | 658 | [[rules]] 659 | description = "Shopify private app access token" 660 | id = "shopify-private-app-access-token" 661 | regex = '''shppa_[a-fA-F0-9]{32}''' 662 | keywords = [ 663 | "shppa_", 664 | ] 665 | 666 | [[rules]] 667 | description = "Shopify shared secret" 668 | id = "shopify-shared-secret" 669 | regex = '''shpss_[a-fA-F0-9]{32}''' 670 | keywords = [ 671 | "shpss_", 672 | ] 673 | 674 | [[rules]] 675 | description = "Slack token" 676 | id = "slack-access-token" 677 | regex = '''xox[baprs]-([0-9a-zA-Z]{10,48})''' 678 | keywords = [ 679 | "xoxb","xoxa","xoxp","xoxr","xoxs", 680 | ] 681 | 682 | [[rules]] 683 | description = "Slack Webhook" 684 | id = "slack-web-hook" 685 | regex = '''https:\/\/hooks.slack.com\/services\/[A-Za-z0-9+\/]{44,46}''' 686 | keywords = [ 687 | "hooks.slack.com", 688 | ] 689 | 690 | [[rules]] 691 | description = "Stripe" 692 | id = "stripe-access-token" 693 | regex = '''(?i)(sk|pk)_(test|live)_[0-9a-z]{10,32}''' 694 | keywords = [ 695 | "sk_test","pk_test","sk_live","pk_live", 696 | ] 697 | 698 | [[rules]] 699 | description = "Twilio API Key" 700 | id = "twilio-api-key" 701 | regex = '''SK[0-9a-fA-F]{32}''' 702 | keywords = [ 703 | "twilio", 704 | ] 705 | 706 | [[rules]] 707 | description = "Twitch API token" 708 | id = "twitch-api-token" 709 | regex = '''(?i)(?:twitch)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-z0-9]{30})(?:['|\"|\n|\r|\s|\x60]|$)''' 710 | secretGroup = 1 711 | keywords = [ 712 | "twitch", 713 | ] 714 | 715 | [[rules]] 716 | description = "twitter" 717 | id = "twitter" 718 | regex = '''(?i)(?:twitter)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([a-f0-9]{35,44})(?:['|\"|\n|\r|\s|\x60]|$)''' 719 | secretGroup = 1 720 | keywords = [ 721 | "twitter", 722 | ] 723 | 724 | [[rules]] 725 | description = "Typeform API token" 726 | id = "typeform-api-token" 727 | regex = '''(?i)(?:typeform)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}(tfp_[a-z0-9\-_\.=]{59})(?:['|\"|\n|\r|\s|\x60]|$)''' 728 | secretGroup = 1 729 | keywords = [ 730 | "tfp_", 731 | ] 732 | 733 | [[rules]] 734 | description = "Generic API Key" 735 | id = "generic-api-key" 736 | regex = '''(?i)(?:key|api[^Version]|token|pat|secret|client|password|auth)(?:[0-9a-z\-_\s.]{0,20})(?:[\s|']|[\s|"]){0,3}(?:=|>|:=|\|\|:|<=|=>|:)(?:'|\"|\s|=|\x60){0,5}([0-9a-z\-_.=]{10,150})(?:['|\"|\n|\r|\s|\x60]|$)''' 737 | secretGroup = 1 738 | entropy = 3.5 739 | keywords = [ 740 | "key","api","token","secret","client","pat","password","auth", 741 | ] 742 | [rules.allowlist] 743 | stopwords= [ 744 | "client", 745 | "endpoint", 746 | "vpn", 747 | "_ec2_", 748 | "aws_", 749 | "authorize", 750 | "author", 751 | "define", 752 | "config", 753 | "credential", 754 | "setting", 755 | "sample", 756 | "xxxxxx", 757 | "000000", 758 | "buffer", 759 | "delete", 760 | "aaaaaa", 761 | "fewfwef", 762 | "getenv", 763 | "env_", 764 | "system", 765 | "example", 766 | "ecdsa", 767 | "sha256", 768 | "sha1", 769 | "sha2", 770 | "md5", 771 | "alert", 772 | "wizard", 773 | "target", 774 | "onboard", 775 | "welcome", 776 | "page", 777 | "exploit", 778 | "experiment", 779 | "expire", 780 | "rabbitmq", 781 | "scraper", 782 | "widget", 783 | "music", 784 | "dns_", 785 | "dns-", 786 | "yahoo", 787 | "want", 788 | "json", 789 | "action", 790 | "script", 791 | "fix_", 792 | "fix-", 793 | "develop", 794 | "compas", 795 | "stripe", 796 | "service", 797 | "master", 798 | "metric", 799 | "tech", 800 | "gitignore", 801 | "rich", 802 | "open", 803 | "stack", 804 | "irc_", 805 | "irc-", 806 | "sublime", 807 | "kohana", 808 | "has_", 809 | "has-", 810 | "fabric", 811 | "wordpres", 812 | "role", 813 | "osx_", 814 | "osx-", 815 | "boost", 816 | "addres", 817 | "queue", 818 | "working", 819 | "sandbox", 820 | "internet", 821 | "print", 822 | "vision", 823 | "tracking", 824 | "being", 825 | "generator", 826 | "traffic", 827 | "world", 828 | "pull", 829 | "rust", 830 | "watcher", 831 | "small", 832 | "auth", 833 | "full", 834 | "hash", 835 | "more", 836 | "install", 837 | "auto", 838 | "complete", 839 | "learn", 840 | "paper", 841 | "installer", 842 | "research", 843 | "acces", 844 | "last", 845 | "binding", 846 | "spine", 847 | "into", 848 | "chat", 849 | "algorithm", 850 | "resource", 851 | "uploader", 852 | "video", 853 | "maker", 854 | "next", 855 | "proc", 856 | "lock", 857 | "robot", 858 | "snake", 859 | "patch", 860 | "matrix", 861 | "drill", 862 | "terminal", 863 | "term", 864 | "stuff", 865 | "genetic", 866 | "generic", 867 | "identity", 868 | "audit", 869 | "pattern", 870 | "audio", 871 | "web_", 872 | "web-", 873 | "crud", 874 | "problem", 875 | "statu", 876 | "cms-", 877 | "cms_", 878 | "arch", 879 | "coffee", 880 | "workflow", 881 | "changelog", 882 | "another", 883 | "uiview", 884 | "content", 885 | "kitchen", 886 | "gnu_", 887 | "gnu-", 888 | "gnu.", 889 | "conf", 890 | "couchdb", 891 | "client", 892 | "opencv", 893 | "rendering", 894 | "update", 895 | "concept", 896 | "varnish", 897 | "gui_", 898 | "gui-", 899 | "gui.", 900 | "version", 901 | "shared", 902 | "extra", 903 | "product", 904 | "still", 905 | "not_", 906 | "not-", 907 | "not.", 908 | "drop", 909 | "ring", 910 | "png_", 911 | "png-", 912 | "png.", 913 | "actively", 914 | "import", 915 | "output", 916 | "backup", 917 | "start", 918 | "embedded", 919 | "registry", 920 | "pool", 921 | "semantic", 922 | "instagram", 923 | "bash", 924 | "system", 925 | "ninja", 926 | "drupal", 927 | "jquery", 928 | "polyfill", 929 | "physic", 930 | "league", 931 | "guide", 932 | "pack", 933 | "synopsi", 934 | "sketch", 935 | "injection", 936 | "svg_", 937 | "svg-", 938 | "svg.", 939 | "friendly", 940 | "wave", 941 | "convert", 942 | "manage", 943 | "camera", 944 | "link", 945 | "slide", 946 | "timer", 947 | "wrapper", 948 | "gallery", 949 | "url_", 950 | "url-", 951 | "url.", 952 | "todomvc", 953 | "requirej", 954 | "party", 955 | "http", 956 | "payment", 957 | "async", 958 | "library", 959 | "home", 960 | "coco", 961 | "gaia", 962 | "display", 963 | "universal", 964 | "function", 965 | "metadata", 966 | "hipchat", 967 | "under", 968 | "room", 969 | "config", 970 | "personal", 971 | "realtime", 972 | "resume", 973 | "database", 974 | "testing", 975 | "tiny", 976 | "basic", 977 | "forum", 978 | "meetup", 979 | "yet_", 980 | "yet-", 981 | "yet.", 982 | "cento", 983 | "dead", 984 | "fluentd", 985 | "editor", 986 | "utilitie", 987 | "run_", 988 | "run-", 989 | "run.", 990 | "box_", 991 | "box-", 992 | "box.", 993 | "bot_", 994 | "bot-", 995 | "bot.", 996 | "making", 997 | "sample", 998 | "group", 999 | "monitor", 1000 | "ajax", 1001 | "parallel", 1002 | "cassandra", 1003 | "ultimate", 1004 | "site", 1005 | "get_", 1006 | "get-", 1007 | "get.", 1008 | "gen_", 1009 | "gen-", 1010 | "gen.", 1011 | "gem_", 1012 | "gem-", 1013 | "gem.", 1014 | "extended", 1015 | "image", 1016 | "knife", 1017 | "asset", 1018 | "nested", 1019 | "zero", 1020 | "plugin", 1021 | "bracket", 1022 | "mule", 1023 | "mozilla", 1024 | "number", 1025 | "act_", 1026 | "act-", 1027 | "act.", 1028 | "map_", 1029 | "map-", 1030 | "map.", 1031 | "micro", 1032 | "debug", 1033 | "openshift", 1034 | "chart", 1035 | "expres", 1036 | "backend", 1037 | "task", 1038 | "source", 1039 | "translate", 1040 | "jbos", 1041 | "composer", 1042 | "sqlite", 1043 | "profile", 1044 | "mustache", 1045 | "mqtt", 1046 | "yeoman", 1047 | "have", 1048 | "builder", 1049 | "smart", 1050 | "like", 1051 | "oauth", 1052 | "school", 1053 | "guideline", 1054 | "captcha", 1055 | "filter", 1056 | "bitcoin", 1057 | "bridge", 1058 | "color", 1059 | "toolbox", 1060 | "discovery", 1061 | "new_", 1062 | "new-", 1063 | "new.", 1064 | "dashboard", 1065 | "when", 1066 | "setting", 1067 | "level", 1068 | "post", 1069 | "standard", 1070 | "port", 1071 | "platform", 1072 | "yui_", 1073 | "yui-", 1074 | "yui.", 1075 | "grunt", 1076 | "animation", 1077 | "haskell", 1078 | "icon", 1079 | "latex", 1080 | "cheat", 1081 | "lua_", 1082 | "lua-", 1083 | "lua.", 1084 | "gulp", 1085 | "case", 1086 | "author", 1087 | "without", 1088 | "simulator", 1089 | "wifi", 1090 | "directory", 1091 | "lisp", 1092 | "list", 1093 | "flat", 1094 | "adventure", 1095 | "story", 1096 | "storm", 1097 | "gpu_", 1098 | "gpu-", 1099 | "gpu.", 1100 | "store", 1101 | "caching", 1102 | "attention", 1103 | "solr", 1104 | "logger", 1105 | "demo", 1106 | "shortener", 1107 | "hadoop", 1108 | "finder", 1109 | "phone", 1110 | "pipeline", 1111 | "range", 1112 | "textmate", 1113 | "showcase", 1114 | "app_", 1115 | "app-", 1116 | "app.", 1117 | "idiomatic", 1118 | "edit", 1119 | "our_", 1120 | "our-", 1121 | "our.", 1122 | "out_", 1123 | "out-", 1124 | "out.", 1125 | "sentiment", 1126 | "linked", 1127 | "why_", 1128 | "why-", 1129 | "why.", 1130 | "local", 1131 | "cube", 1132 | "gmail", 1133 | "job_", 1134 | "job-", 1135 | "job.", 1136 | "rpc_", 1137 | "rpc-", 1138 | "rpc.", 1139 | "contest", 1140 | "tcp_", 1141 | "tcp-", 1142 | "tcp.", 1143 | "usage", 1144 | "buildout", 1145 | "weather", 1146 | "transfer", 1147 | "automated", 1148 | "sphinx", 1149 | "issue", 1150 | "sas_", 1151 | "sas-", 1152 | "sas.", 1153 | "parallax", 1154 | "jasmine", 1155 | "addon", 1156 | "machine", 1157 | "solution", 1158 | "dsl_", 1159 | "dsl-", 1160 | "dsl.", 1161 | "episode", 1162 | "menu", 1163 | "theme", 1164 | "best", 1165 | "adapter", 1166 | "debugger", 1167 | "chrome", 1168 | "tutorial", 1169 | "life", 1170 | "step", 1171 | "people", 1172 | "joomla", 1173 | "paypal", 1174 | "developer", 1175 | "solver", 1176 | "team", 1177 | "current", 1178 | "love", 1179 | "visual", 1180 | "date", 1181 | "data", 1182 | "canva", 1183 | "container", 1184 | "future", 1185 | "xml_", 1186 | "xml-", 1187 | "xml.", 1188 | "twig", 1189 | "nagio", 1190 | "spatial", 1191 | "original", 1192 | "sync", 1193 | "archived", 1194 | "refinery", 1195 | "science", 1196 | "mapping", 1197 | "gitlab", 1198 | "play", 1199 | "ext_", 1200 | "ext-", 1201 | "ext.", 1202 | "session", 1203 | "impact", 1204 | "set_", 1205 | "set-", 1206 | "set.", 1207 | "see_", 1208 | "see-", 1209 | "see.", 1210 | "migration", 1211 | "commit", 1212 | "community", 1213 | "shopify", 1214 | "what'", 1215 | "cucumber", 1216 | "statamic", 1217 | "mysql", 1218 | "location", 1219 | "tower", 1220 | "line", 1221 | "code", 1222 | "amqp", 1223 | "hello", 1224 | "send", 1225 | "index", 1226 | "high", 1227 | "notebook", 1228 | "alloy", 1229 | "python", 1230 | "field", 1231 | "document", 1232 | "soap", 1233 | "edition", 1234 | "email", 1235 | "php_", 1236 | "php-", 1237 | "php.", 1238 | "command", 1239 | "transport", 1240 | "official", 1241 | "upload", 1242 | "study", 1243 | "secure", 1244 | "angularj", 1245 | "akka", 1246 | "scalable", 1247 | "package", 1248 | "request", 1249 | "con_", 1250 | "con-", 1251 | "con.", 1252 | "flexible", 1253 | "security", 1254 | "comment", 1255 | "module", 1256 | "flask", 1257 | "graph", 1258 | "flash", 1259 | "apache", 1260 | "change", 1261 | "window", 1262 | "space", 1263 | "lambda", 1264 | "sheet", 1265 | "bookmark", 1266 | "carousel", 1267 | "friend", 1268 | "objective", 1269 | "jekyll", 1270 | "bootstrap", 1271 | "first", 1272 | "article", 1273 | "gwt_", 1274 | "gwt-", 1275 | "gwt.", 1276 | "classic", 1277 | "media", 1278 | "websocket", 1279 | "touch", 1280 | "desktop", 1281 | "real", 1282 | "read", 1283 | "recorder", 1284 | "moved", 1285 | "storage", 1286 | "validator", 1287 | "add-on", 1288 | "pusher", 1289 | "scs_", 1290 | "scs-", 1291 | "scs.", 1292 | "inline", 1293 | "asp_", 1294 | "asp-", 1295 | "asp.", 1296 | "timeline", 1297 | "base", 1298 | "encoding", 1299 | "ffmpeg", 1300 | "kindle", 1301 | "tinymce", 1302 | "pretty", 1303 | "jpa_", 1304 | "jpa-", 1305 | "jpa.", 1306 | "used", 1307 | "user", 1308 | "required", 1309 | "webhook", 1310 | "download", 1311 | "resque", 1312 | "espresso", 1313 | "cloud", 1314 | "mongo", 1315 | "benchmark", 1316 | "pure", 1317 | "cakephp", 1318 | "modx", 1319 | "mode", 1320 | "reactive", 1321 | "fuel", 1322 | "written", 1323 | "flickr", 1324 | "mail", 1325 | "brunch", 1326 | "meteor", 1327 | "dynamic", 1328 | "neo_", 1329 | "neo-", 1330 | "neo.", 1331 | "new_", 1332 | "new-", 1333 | "new.", 1334 | "net_", 1335 | "net-", 1336 | "net.", 1337 | "typo", 1338 | "type", 1339 | "keyboard", 1340 | "erlang", 1341 | "adobe", 1342 | "logging", 1343 | "ckeditor", 1344 | "message", 1345 | "iso_", 1346 | "iso-", 1347 | "iso.", 1348 | "hook", 1349 | "ldap", 1350 | "folder", 1351 | "reference", 1352 | "railscast", 1353 | "www_", 1354 | "www-", 1355 | "www.", 1356 | "tracker", 1357 | "azure", 1358 | "fork", 1359 | "form", 1360 | "digital", 1361 | "exporter", 1362 | "skin", 1363 | "string", 1364 | "template", 1365 | "designer", 1366 | "gollum", 1367 | "fluent", 1368 | "entity", 1369 | "language", 1370 | "alfred", 1371 | "summary", 1372 | "wiki", 1373 | "kernel", 1374 | "calendar", 1375 | "plupload", 1376 | "symfony", 1377 | "foundry", 1378 | "remote", 1379 | "talk", 1380 | "search", 1381 | "dev_", 1382 | "dev-", 1383 | "dev.", 1384 | "del_", 1385 | "del-", 1386 | "del.", 1387 | "token", 1388 | "idea", 1389 | "sencha", 1390 | "selector", 1391 | "interface", 1392 | "create", 1393 | "fun_", 1394 | "fun-", 1395 | "fun.", 1396 | "groovy", 1397 | "query", 1398 | "grail", 1399 | "red_", 1400 | "red-", 1401 | "red.", 1402 | "laravel", 1403 | "monkey", 1404 | "slack", 1405 | "supported", 1406 | "instant", 1407 | "value", 1408 | "center", 1409 | "latest", 1410 | "work", 1411 | "but_", 1412 | "but-", 1413 | "but.", 1414 | "bug_", 1415 | "bug-", 1416 | "bug.", 1417 | "virtual", 1418 | "tweet", 1419 | "statsd", 1420 | "studio", 1421 | "path", 1422 | "real-time", 1423 | "frontend", 1424 | "notifier", 1425 | "coding", 1426 | "tool", 1427 | "firmware", 1428 | "flow", 1429 | "random", 1430 | "mediawiki", 1431 | "bosh", 1432 | "been", 1433 | "beer", 1434 | "lightbox", 1435 | "theory", 1436 | "origin", 1437 | "redmine", 1438 | "hub_", 1439 | "hub-", 1440 | "hub.", 1441 | "require", 1442 | "pro_", 1443 | "pro-", 1444 | "pro.", 1445 | "ant_", 1446 | "ant-", 1447 | "ant.", 1448 | "any_", 1449 | "any-", 1450 | "any.", 1451 | "recipe", 1452 | "closure", 1453 | "mapper", 1454 | "event", 1455 | "todo", 1456 | "model", 1457 | "redi", 1458 | "provider", 1459 | "rvm_", 1460 | "rvm-", 1461 | "rvm.", 1462 | "program", 1463 | "memcached", 1464 | "rail", 1465 | "silex", 1466 | "foreman", 1467 | "activity", 1468 | "license", 1469 | "strategy", 1470 | "batch", 1471 | "streaming", 1472 | "fast", 1473 | "use_", 1474 | "use-", 1475 | "use.", 1476 | "usb_", 1477 | "usb-", 1478 | "usb.", 1479 | "impres", 1480 | "academy", 1481 | "slider", 1482 | "please", 1483 | "layer", 1484 | "cros", 1485 | "now_", 1486 | "now-", 1487 | "now.", 1488 | "miner", 1489 | "extension", 1490 | "own_", 1491 | "own-", 1492 | "own.", 1493 | "app_", 1494 | "app-", 1495 | "app.", 1496 | "debian", 1497 | "symphony", 1498 | "example", 1499 | "feature", 1500 | "serie", 1501 | "tree", 1502 | "project", 1503 | "runner", 1504 | "entry", 1505 | "leetcode", 1506 | "layout", 1507 | "webrtc", 1508 | "logic", 1509 | "login", 1510 | "worker", 1511 | "toolkit", 1512 | "mocha", 1513 | "support", 1514 | "back", 1515 | "inside", 1516 | "device", 1517 | "jenkin", 1518 | "contact", 1519 | "fake", 1520 | "awesome", 1521 | "ocaml", 1522 | "bit_", 1523 | "bit-", 1524 | "bit.", 1525 | "drive", 1526 | "screen", 1527 | "prototype", 1528 | "gist", 1529 | "binary", 1530 | "nosql", 1531 | "rest", 1532 | "overview", 1533 | "dart", 1534 | "dark", 1535 | "emac", 1536 | "mongoid", 1537 | "solarized", 1538 | "homepage", 1539 | "emulator", 1540 | "commander", 1541 | "django", 1542 | "yandex", 1543 | "gradle", 1544 | "xcode", 1545 | "writer", 1546 | "crm_", 1547 | "crm-", 1548 | "crm.", 1549 | "jade", 1550 | "startup", 1551 | "error", 1552 | "using", 1553 | "format", 1554 | "name", 1555 | "spring", 1556 | "parser", 1557 | "scratch", 1558 | "magic", 1559 | "try_", 1560 | "try-", 1561 | "try.", 1562 | "rack", 1563 | "directive", 1564 | "challenge", 1565 | "slim", 1566 | "counter", 1567 | "element", 1568 | "chosen", 1569 | "doc_", 1570 | "doc-", 1571 | "doc.", 1572 | "meta", 1573 | "should", 1574 | "button", 1575 | "packet", 1576 | "stream", 1577 | "hardware", 1578 | "android", 1579 | "infinite", 1580 | "password", 1581 | "software", 1582 | "ghost", 1583 | "xamarin", 1584 | "spec", 1585 | "chef", 1586 | "interview", 1587 | "hubot", 1588 | "mvc_", 1589 | "mvc-", 1590 | "mvc.", 1591 | "exercise", 1592 | "leaflet", 1593 | "launcher", 1594 | "air_", 1595 | "air-", 1596 | "air.", 1597 | "photo", 1598 | "board", 1599 | "boxen", 1600 | "way_", 1601 | "way-", 1602 | "way.", 1603 | "computing", 1604 | "welcome", 1605 | "notepad", 1606 | "portfolio", 1607 | "cat_", 1608 | "cat-", 1609 | "cat.", 1610 | "can_", 1611 | "can-", 1612 | "can.", 1613 | "magento", 1614 | "yaml", 1615 | "domain", 1616 | "card", 1617 | "yii_", 1618 | "yii-", 1619 | "yii.", 1620 | "checker", 1621 | "browser", 1622 | "upgrade", 1623 | "only", 1624 | "progres", 1625 | "aura", 1626 | "ruby_", 1627 | "ruby-", 1628 | "ruby.", 1629 | "polymer", 1630 | "util", 1631 | "lite", 1632 | "hackathon", 1633 | "rule", 1634 | "log_", 1635 | "log-", 1636 | "log.", 1637 | "opengl", 1638 | "stanford", 1639 | "skeleton", 1640 | "history", 1641 | "inspector", 1642 | "help", 1643 | "soon", 1644 | "selenium", 1645 | "lab_", 1646 | "lab-", 1647 | "lab.", 1648 | "scheme", 1649 | "schema", 1650 | "look", 1651 | "ready", 1652 | "leveldb", 1653 | "docker", 1654 | "game", 1655 | "minimal", 1656 | "logstash", 1657 | "messaging", 1658 | "within", 1659 | "heroku", 1660 | "mongodb", 1661 | "kata", 1662 | "suite", 1663 | "picker", 1664 | "win_", 1665 | "win-", 1666 | "win.", 1667 | "wip_", 1668 | "wip-", 1669 | "wip.", 1670 | "panel", 1671 | "started", 1672 | "starter", 1673 | "front-end", 1674 | "detector", 1675 | "deploy", 1676 | "editing", 1677 | "based", 1678 | "admin", 1679 | "capture", 1680 | "spree", 1681 | "page", 1682 | "bundle", 1683 | "goal", 1684 | "rpg_", 1685 | "rpg-", 1686 | "rpg.", 1687 | "setup", 1688 | "side", 1689 | "mean", 1690 | "reader", 1691 | "cookbook", 1692 | "mini", 1693 | "modern", 1694 | "seed", 1695 | "dom_", 1696 | "dom-", 1697 | "dom.", 1698 | "doc_", 1699 | "doc-", 1700 | "doc.", 1701 | "dot_", 1702 | "dot-", 1703 | "dot.", 1704 | "syntax", 1705 | "sugar", 1706 | "loader", 1707 | "website", 1708 | "make", 1709 | "kit_", 1710 | "kit-", 1711 | "kit.", 1712 | "protocol", 1713 | "human", 1714 | "daemon", 1715 | "golang", 1716 | "manager", 1717 | "countdown", 1718 | "connector", 1719 | "swagger", 1720 | "map_", 1721 | "map-", 1722 | "map.", 1723 | "mac_", 1724 | "mac-", 1725 | "mac.", 1726 | "man_", 1727 | "man-", 1728 | "man.", 1729 | "orm_", 1730 | "orm-", 1731 | "orm.", 1732 | "org_", 1733 | "org-", 1734 | "org.", 1735 | "little", 1736 | "zsh_", 1737 | "zsh-", 1738 | "zsh.", 1739 | "shop", 1740 | "show", 1741 | "workshop", 1742 | "money", 1743 | "grid", 1744 | "server", 1745 | "octopres", 1746 | "svn_", 1747 | "svn-", 1748 | "svn.", 1749 | "ember", 1750 | "embed", 1751 | "general", 1752 | "file", 1753 | "important", 1754 | "dropbox", 1755 | "portable", 1756 | "public", 1757 | "docpad", 1758 | "fish", 1759 | "sbt_", 1760 | "sbt-", 1761 | "sbt.", 1762 | "done", 1763 | "para", 1764 | "network", 1765 | "common", 1766 | "readme", 1767 | "popup", 1768 | "simple", 1769 | "purpose", 1770 | "mirror", 1771 | "single", 1772 | "cordova", 1773 | "exchange", 1774 | "object", 1775 | "design", 1776 | "gateway", 1777 | "account", 1778 | "lamp", 1779 | "intellij", 1780 | "math", 1781 | "mit_", 1782 | "mit-", 1783 | "mit.", 1784 | "control", 1785 | "enhanced", 1786 | "emitter", 1787 | "multi", 1788 | "add_", 1789 | "add-", 1790 | "add.", 1791 | "about", 1792 | "socket", 1793 | "preview", 1794 | "vagrant", 1795 | "cli_", 1796 | "cli-", 1797 | "cli.", 1798 | "powerful", 1799 | "top_", 1800 | "top-", 1801 | "top.", 1802 | "radio", 1803 | "watch", 1804 | "fluid", 1805 | "amazon", 1806 | "report", 1807 | "couchbase", 1808 | "automatic", 1809 | "detection", 1810 | "sprite", 1811 | "pyramid", 1812 | "portal", 1813 | "advanced", 1814 | "plu_", 1815 | "plu-", 1816 | "plu.", 1817 | "runtime", 1818 | "git_", 1819 | "git-", 1820 | "git.", 1821 | "uri_", 1822 | "uri-", 1823 | "uri.", 1824 | "haml", 1825 | "node", 1826 | "sql_", 1827 | "sql-", 1828 | "sql.", 1829 | "cool", 1830 | "core", 1831 | "obsolete", 1832 | "handler", 1833 | "iphone", 1834 | "extractor", 1835 | "array", 1836 | "copy", 1837 | "nlp_", 1838 | "nlp-", 1839 | "nlp.", 1840 | "reveal", 1841 | "pop_", 1842 | "pop-", 1843 | "pop.", 1844 | "engine", 1845 | "parse", 1846 | "check", 1847 | "html", 1848 | "nest", 1849 | "all_", 1850 | "all-", 1851 | "all.", 1852 | "chinese", 1853 | "buildpack", 1854 | "what", 1855 | "tag_", 1856 | "tag-", 1857 | "tag.", 1858 | "proxy", 1859 | "style", 1860 | "cookie", 1861 | "feed", 1862 | "restful", 1863 | "compiler", 1864 | "creating", 1865 | "prelude", 1866 | "context", 1867 | "java", 1868 | "rspec", 1869 | "mock", 1870 | "backbone", 1871 | "light", 1872 | "spotify", 1873 | "flex", 1874 | "related", 1875 | "shell", 1876 | "which", 1877 | "clas", 1878 | "webapp", 1879 | "swift", 1880 | "ansible", 1881 | "unity", 1882 | "console", 1883 | "tumblr", 1884 | "export", 1885 | "campfire", 1886 | "conway'", 1887 | "made", 1888 | "riak", 1889 | "hero", 1890 | "here", 1891 | "unix", 1892 | "unit", 1893 | "glas", 1894 | "smtp", 1895 | "how_", 1896 | "how-", 1897 | "how.", 1898 | "hot_", 1899 | "hot-", 1900 | "hot.", 1901 | "debug", 1902 | "release", 1903 | "diff", 1904 | "player", 1905 | "easy", 1906 | "right", 1907 | "old_", 1908 | "old-", 1909 | "old.", 1910 | "animate", 1911 | "time", 1912 | "push", 1913 | "explorer", 1914 | "course", 1915 | "training", 1916 | "nette", 1917 | "router", 1918 | "draft", 1919 | "structure", 1920 | "note", 1921 | "salt", 1922 | "where", 1923 | "spark", 1924 | "trello", 1925 | "power", 1926 | "method", 1927 | "social", 1928 | "via_", 1929 | "via-", 1930 | "via.", 1931 | "vim_", 1932 | "vim-", 1933 | "vim.", 1934 | "select", 1935 | "webkit", 1936 | "github", 1937 | "ftp_", 1938 | "ftp-", 1939 | "ftp.", 1940 | "creator", 1941 | "mongoose", 1942 | "led_", 1943 | "led-", 1944 | "led.", 1945 | "movie", 1946 | "currently", 1947 | "pdf_", 1948 | "pdf-", 1949 | "pdf.", 1950 | "load", 1951 | "markdown", 1952 | "phalcon", 1953 | "input", 1954 | "custom", 1955 | "atom", 1956 | "oracle", 1957 | "phonegap", 1958 | "ubuntu", 1959 | "great", 1960 | "rdf_", 1961 | "rdf-", 1962 | "rdf.", 1963 | "popcorn", 1964 | "firefox", 1965 | "zip_", 1966 | "zip-", 1967 | "zip.", 1968 | "cuda", 1969 | "dotfile", 1970 | "static", 1971 | "openwrt", 1972 | "viewer", 1973 | "powered", 1974 | "graphic", 1975 | "les_", 1976 | "les-", 1977 | "les.", 1978 | "doe_", 1979 | "doe-", 1980 | "doe.", 1981 | "maven", 1982 | "word", 1983 | "eclipse", 1984 | "lab_", 1985 | "lab-", 1986 | "lab.", 1987 | "hacking", 1988 | "steam", 1989 | "analytic", 1990 | "option", 1991 | "abstract", 1992 | "archive", 1993 | "reality", 1994 | "switcher", 1995 | "club", 1996 | "write", 1997 | "kafka", 1998 | "arduino", 1999 | "angular", 2000 | "online", 2001 | "title", 2002 | "don't", 2003 | "contao", 2004 | "notice", 2005 | "analyzer", 2006 | "learning", 2007 | "zend", 2008 | "external", 2009 | "staging", 2010 | "busines", 2011 | "tdd_", 2012 | "tdd-", 2013 | "tdd.", 2014 | "scanner", 2015 | "building", 2016 | "snippet", 2017 | "modular", 2018 | "bower", 2019 | "stm_", 2020 | "stm-", 2021 | "stm.", 2022 | "lib_", 2023 | "lib-", 2024 | "lib.", 2025 | "alpha", 2026 | "mobile", 2027 | "clean", 2028 | "linux", 2029 | "nginx", 2030 | "manifest", 2031 | "some", 2032 | "raspberry", 2033 | "gnome", 2034 | "ide_", 2035 | "ide-", 2036 | "ide.", 2037 | "block", 2038 | "statistic", 2039 | "info", 2040 | "drag", 2041 | "youtube", 2042 | "koan", 2043 | "facebook", 2044 | "paperclip", 2045 | "art_", 2046 | "art-", 2047 | "art.", 2048 | "quality", 2049 | "tab_", 2050 | "tab-", 2051 | "tab.", 2052 | "need", 2053 | "dojo", 2054 | "shield", 2055 | "computer", 2056 | "stat", 2057 | "state", 2058 | "twitter", 2059 | "utility", 2060 | "converter", 2061 | "hosting", 2062 | "devise", 2063 | "liferay", 2064 | "updated", 2065 | "force", 2066 | "tip_", 2067 | "tip-", 2068 | "tip.", 2069 | "behavior", 2070 | "active", 2071 | "call", 2072 | "answer", 2073 | "deck", 2074 | "better", 2075 | "principle", 2076 | "ches", 2077 | "bar_", 2078 | "bar-", 2079 | "bar.", 2080 | "reddit", 2081 | "three", 2082 | "haxe", 2083 | "just", 2084 | "plug-in", 2085 | "agile", 2086 | "manual", 2087 | "tetri", 2088 | "super", 2089 | "beta", 2090 | "parsing", 2091 | "doctrine", 2092 | "minecraft", 2093 | "useful", 2094 | "perl", 2095 | "sharing", 2096 | "agent", 2097 | "switch", 2098 | "view", 2099 | "dash", 2100 | "channel", 2101 | "repo", 2102 | "pebble", 2103 | "profiler", 2104 | "warning", 2105 | "cluster", 2106 | "running", 2107 | "markup", 2108 | "evented", 2109 | "mod_", 2110 | "mod-", 2111 | "mod.", 2112 | "share", 2113 | "csv_", 2114 | "csv-", 2115 | "csv.", 2116 | "response", 2117 | "good", 2118 | "house", 2119 | "connect", 2120 | "built", 2121 | "build", 2122 | "find", 2123 | "ipython", 2124 | "webgl", 2125 | "big_", 2126 | "big-", 2127 | "big.", 2128 | "google", 2129 | "scala", 2130 | "sdl_", 2131 | "sdl-", 2132 | "sdl.", 2133 | "sdk_", 2134 | "sdk-", 2135 | "sdk.", 2136 | "native", 2137 | "day_", 2138 | "day-", 2139 | "day.", 2140 | "puppet", 2141 | "text", 2142 | "routing", 2143 | "helper", 2144 | "linkedin", 2145 | "crawler", 2146 | "host", 2147 | "guard", 2148 | "merchant", 2149 | "poker", 2150 | "over", 2151 | "writing", 2152 | "free", 2153 | "classe", 2154 | "component", 2155 | "craft", 2156 | "nodej", 2157 | "phoenix", 2158 | "longer", 2159 | "quick", 2160 | "lazy", 2161 | "memory", 2162 | "clone", 2163 | "hacker", 2164 | "middleman", 2165 | "factory", 2166 | "motion", 2167 | "multiple", 2168 | "tornado", 2169 | "hack", 2170 | "ssh_", 2171 | "ssh-", 2172 | "ssh.", 2173 | "review", 2174 | "vimrc", 2175 | "driver", 2176 | "driven", 2177 | "blog", 2178 | "particle", 2179 | "table", 2180 | "intro", 2181 | "importer", 2182 | "thrift", 2183 | "xmpp", 2184 | "framework", 2185 | "refresh", 2186 | "react", 2187 | "font", 2188 | "librarie", 2189 | "variou", 2190 | "formatter", 2191 | "analysi", 2192 | "karma", 2193 | "scroll", 2194 | "tut_", 2195 | "tut-", 2196 | "tut.", 2197 | "apple", 2198 | "tag_", 2199 | "tag-", 2200 | "tag.", 2201 | "tab_", 2202 | "tab-", 2203 | "tab.", 2204 | "category", 2205 | "ionic", 2206 | "cache", 2207 | "homebrew", 2208 | "reverse", 2209 | "english", 2210 | "getting", 2211 | "shipping", 2212 | "clojure", 2213 | "boot", 2214 | "book", 2215 | "branch", 2216 | ] 2217 | --------------------------------------------------------------------------------