Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
eshaan7 committed Dec 23, 2020
0 parents commit b8cc3ba
Show file tree
Hide file tree
Showing 24 changed files with 718 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[run]
branch = True
source = click-creds
omit =
example_project/*

[report]
exclude_lines =
if self.debug:
pragma: no cover
raise NotImplementedError
if __name__ == .__main__.:
ignore_errors = True
omit =
example_project/*
tests/*
9 changes: 9 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[flake8]
max-line-length = 88
exclude =
*migrations*
*venv*
virtualenv
docs
.tox
isort*
5 changes: 5 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## 0.0.1 (https://github.com/eshaan7/click-creds/releases/tag/0.0.1)

Initial Release.
26 changes: 26 additions & 0 deletions .github/workflows/pythonpublish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Upload Python Package

on:
release:
types: [created]

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
39 changes: 39 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Linter & Tests

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 4
matrix:
python-version: ['3.6', '3.7', '3.8', '3.9']

steps:
- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install tox tox-gh-actions flake8 codecov black==20.8b1
- name: Lint with flake8 and black
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count
black . --check
- name: Run tox tests
run: |
# run tests with coverage
tox
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.idea
build
dist
*egg-info
venv/
*.exe
__pycache__
.vscode
docs/_build/
17 changes: 17 additions & 0 deletions .lgtm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
queries:
- exclude: py/similar-function
- exclude: py/empty-except
- exclude: py/call-to-non-callable
- include: py/undefined-placeholder-variable
- include: py/uninitialized-local-variable
- include: py/request-without-cert-validation
- include: py/return-or-yield-outside-function
- include: py/file-not-closed
- include: py/exit-from-finally
- include: py/ineffectual-statement
- include: py/unused-global-variable
- include: py/hardcoded-credentials
- include: py/import-of-mutable-attribute
- include: py/cyclic-import
- include: py/unnecessary-lambda
- include: py/print-during-import
9 changes: 9 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
repos:
- repo: https://github.com/psf/black
rev: 20.8b1
hooks:
- id: black
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.4
hooks:
- id: flake8
29 changes: 29 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) 2020, Eshaan Bansal
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include README.md
include version.txt
include requirements.dev.txt
include LICENSE
76 changes: 76 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# click-creds

[![pypi](https://img.shields.io/pypi/v/click-creds)](https://pypi.org/project/click-creds/)
[![Build Status](https://github.com/Eshaan7/click-creds/workflows/Linter%20&%20Tests/badge.svg?branch=main)](https://github.com/Eshaan7/click-creds/actions?query=workflow%3A%22Linter+%26+Tests%22)
[![codecov](https://codecov.io/gh/Eshaan7/click-creds/branch/main/graph/badge.svg?token=bh4tGXy8dK)](https://codecov.io/gh/Eshaan7/click-creds)
[![CodeFactor](https://www.codefactor.io/repository/github/eshaan7/click-creds/badge)](https://www.codefactor.io/repository/github/eshaan7/click-creds)
<a href="https://lgtm.com/projects/g/Eshaan7/click-creds/context:python">
<img alt="Language grade: Python" src="https://img.shields.io/lgtm/grade/python/g/Eshaan7/click-creds.svg?logo=lgtm&logoWidth=18"/>
</a>


Pluggable credentials storage and management for [click](https://github.com/pallets/click/) CLI applications.

Uses [`~/.netrc` file method](https://www.mkssoftware.com/docs/man4/netrc.4.asp) which is used by popular CLI applications like [Heroku CLI](https://devcenter.heroku.com/articles/authentication#netrc-file-format), AWS CLIs, etc.

## Installation

```bash
$ pip install click-creds
```

## Quickstart

Here's an example `cli.py` file.

```python
#!/usr/bin/env python3
import click
import click_creds

@click.group(context_settings=dict(help_option_names=["-h", "--help"]))
@click_creds.use_netrccreds(
name="myawesomeapp",
mapping={"login": "username", "password": "api_key", "account": "url"},
)
def cli():
pass

# Register "config" group
cli.add_command(click_creds.config_group)

# Entrypoint
if __name__ == "__main__":
cli()
```

Now, if we execute `./cli.py config`,

```bash
$ ./cli.py config
Usage: cli.py config [OPTIONS] COMMAND [ARGS]...

Set or view config variables

Options:
-h, --help Show this message and exit.

Commands:
get Echo config variables
set Update config variables
```


## Changelog / Releases

All releases should be listed in the [releases tab on GitHub](https://github.com/Eshaan7/click-creds/releases).

See [CHANGELOG](https://github.com/Eshaan7/click-creds/blob/main/.github/CHANGELOG.md) for a more detailed listing.

## License

This project is published with the [BSD License](LICENSE). See [https://choosealicense.com/licenses/bsd/](https://choosealicense.com/licenses/BSD/) for more information about what this means.

## Credits

- [tinynetrc](https://github.com/sloria/tinynetrc)
13 changes: 13 additions & 0 deletions click_creds/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# flake8: noqa
"""
click_creds
~~~~~~~~~~~~~~~~~~~~~~~~~~~
click-creds
:copyright: (c) 2020 by Eshaan Bansal.
:license: BSD, see LICENSE for more details.
"""

from .classes import NetrcStore
from .decorators import use_netrcstore, pass_netrcstore_obj
from .groups import config_group
from .utils import get_netrc_object_from_ctx
62 changes: 62 additions & 0 deletions click_creds/classes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import pathlib
from tinynetrc import Netrc as _TinyNetrc

from .defaults import DEFAULT_MAPPING
from .utils import ThreadSafeSingleton


class ClickCreds(metaclass=ThreadSafeSingleton):
pass


class NetrcStore(ClickCreds):
"""
A tiny wrapper class over the :class:`tinynetrc.Netrc` class.
"""

__name: str
__mapping: dict

def __init__(self, name: str, mapping: dict = DEFAULT_MAPPING):
"""Create a NetrcStore instance.
Args:
name (str):
host name
mapping (dict, optional):
custom mapping for the netrc's host dictionary.
Defaults to DEFAULT_MAPPING.
"""
self.__name = name
self.__mapping = mapping

@property
def mapping(self) -> dict:
"""
Current host mapping as a dictionary.
"""
return self.__mapping

@property
def __netrc_obj(self) -> _TinyNetrc:
if not hasattr(self, "__internal_netrc_obj"):
filepath = pathlib.Path().home().joinpath(".netrc")
filepath.touch(exist_ok=True)
self.__internal_netrc_obj = _TinyNetrc(str(filepath))

return self.__internal_netrc_obj

@property
def host_with_mapping(self) -> dict:
host_dict = self.host
return {self.mapping[k]: v for k, v in host_dict.items()}

@property
def host(self) -> dict:
host_dict = self.__netrc_obj[self.__name]
return host_dict

def save(self, host: dict) -> None:
obj = self.__netrc_obj
obj[self.__name] = host
obj.save()
32 changes: 32 additions & 0 deletions click_creds/decorators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import click
from functools import update_wrapper

from .classes import NetrcStore
from .defaults import DEFAULT_MAPPING, MODULE_KEY


def use_netrcstore(name, mapping=DEFAULT_MAPPING):
"""Sets :attr:`click.Context.obj` to a :class:`NetrcStore` instance."""

def decorator(f):
@click.pass_context
def new_func(ctx: click.Context, *args, **kwargs):
ctx.meta[MODULE_KEY] = NetrcStore(name, mapping=mapping)
return ctx.invoke(f, *args, **kwargs)

return update_wrapper(new_func, f)

return decorator


def pass_netrcstore_obj(f):
"""Similar to :func:`click.pass_obj`,
this passes the current :class:`NetrcStore` instance as the first argument.
"""

@click.pass_context
def new_func(ctx, *args, **kwargs):
netrc_obj = ctx.meta[MODULE_KEY]
return ctx.invoke(f, netrc_obj, *args, **kwargs)

return update_wrapper(new_func, f)
7 changes: 7 additions & 0 deletions click_creds/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
DEFAULT_MAPPING = {
"login": "login",
"password": "password",
"account": "account",
}

MODULE_KEY = "click_creds.classes.ClickCreds"
Loading

0 comments on commit b8cc3ba

Please sign in to comment.