Merge pull request #39 from jedie/devshell

WIP: Use https://github.com/jedie/dev-shell
pull/40/head
Jens Diemer 2021-04-05 21:16:34 +02:00 zatwierdzone przez GitHub
commit 81d2a7f36e
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
16 zmienionych plików z 922 dodań i 674 usunięć

Wyświetl plik

@ -2,6 +2,6 @@
# Move to pyproject.toml after: https://gitlab.com/pycqa/flake8/-/issues/428
#
[flake8]
exclude = .pytest_cache, .tox, dist, htmlcov, */migrations/*, volumes
exclude = .*, dist, htmlcov, */migrations/*, volumes
#ignore = E402
max-line-length = 119

Wyświetl plik

@ -1,41 +1,47 @@
name: test
# https://github.com/actions/setup-python
name: Test
on:
push:
schedule:
- cron: '0 8 * * *'
push:
jobs:
test:
runs-on: ubuntu-latest
name: 'Python ${{ matrix.python-version }} on ${{ matrix.os }}'
runs-on: ${{ matrix.os }}
env:
PYTHONUNBUFFERED: 1
strategy:
max-parallel: 2
fail-fast: false
matrix:
python-version: [3.9, 3.8, 3.7]
os: [ubuntu-latest] # TODO: macOS-latest, windows-latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: 'Set up Python ${{ matrix.python-version }}'
uses: actions/setup-python@v1
uses: actions/setup-python@v2
with:
python-version: '${{ matrix.python-version }}'
- name: 'Install package'
- name: 'Bootstrap'
run: |
pip3 install poetry
make install
python3 devshell.py quit
- name: 'List installed packages'
run: |
poetry run pip freeze
python3 devshell.py list_venv_packages
- name: 'Run tests with Python v${{ matrix.python-version }}'
run: |
poetry run pytest
python3 devshell.py pytest -vv
- name: 'Upload coverage report'
if: matrix.os == 'ubuntu-latest'
run: bash <(curl -s https://codecov.io/bash)
- name: 'Run linters'
if: matrix.python-version == '3.8'
run: |
make lint
python3 devshell.py linting

1
.gitignore vendored
Wyświetl plik

@ -1,4 +1,5 @@
.*
!.github
!.flake8
!.gitignore
!.isort.cfg

112
Makefile
Wyświetl plik

@ -1,112 +0,0 @@
SHELL := /bin/bash
MAX_LINE_LENGTH := 119
export DJANGO_SETTINGS_MODULE ?= inventory_project.settings.local
all: help
help:
@echo -e '_________________________________________________________________'
@echo -e 'PyInventory - *dev* Makefile\n'
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9 -]+:.*?## / {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
check-poetry:
@if [[ "$(shell poetry --version 2>/dev/null)" == *"Poetry"* ]] ; \
then \
echo "Poetry found, ok." ; \
else \
echo 'Please install poetry first, with e.g.:' ; \
echo 'make install-poetry' ; \
exit 1 ; \
fi
install-poetry: ## install or update poetry
pip3 install -U pip
pip3 install -U poetry
install: check-poetry ## install PyInventory via poetry
poetry install
manage-update: ## Collectstatic + makemigration + migrate
./manage.sh collectstatic --noinput
./manage.sh makemigrations
./manage.sh migrate
update: check-poetry ## update the sources and installation
git fetch --all
git pull origin master
poetry run pip install -U pip
poetry update
lint: ## Run code formatters and linter
poetry run flynt -e "volumes" -e "htmlcov" --fail-on-change --line_length=${MAX_LINE_LENGTH} .
poetry run isort --check-only .
poetry run flake8 .
fix-code-style: ## Fix code formatting
poetry run flynt -e "volumes" -e "htmlcov" --line_length=${MAX_LINE_LENGTH} .
poetry run pyupgrade --exit-zero-even-if-changed --py3-plus --py36-plus --py37-plus `find . -name "*.py" -type f ! -path "./.tox/*" ! -path "./htmlcov/*" ! -path "*/volumes/*" 2>/dev/null`
poetry run isort .
poetry run autopep8 --aggressive --aggressive --in-place --recursive .
tox-listenvs: check-poetry ## List all tox test environments
poetry run tox --listenvs
tox: check-poetry ## Run pytest via tox with all environments
poetry run tox
tox-py36: check-poetry ## Run pytest via tox with *python v3.6*
poetry run tox -e py36
tox-py37: check-poetry ## Run pytest via tox with *python v3.7*
poetry run tox -e py37
tox-py38: check-poetry ## Run pytest via tox with *python v3.8*
poetry run tox -e py38
pytest: check-poetry ## Run pytest
poetry run pytest --workers auto --tests-per-worker 1
update-rst-readme: ## update README.rst from README.creole
poetry run update_rst_readme
publish: ## Release new version to PyPi
poetry run publish
run-dev-server: ## Run the django dev server in endless loop.
./manage.sh collectstatic --noinput --link
./manage.sh migrate
./manage.sh runserver
createsuperuser: ## Create super user
./manage.sh createsuperuser
messages: ## Make and compile locales message files
./manage.sh makemessages --all --no-location --no-obsolete --ignore=htmlcov --ignore=.tox --ignore=volumes
./manage.sh compilemessages -v 0
##############################################################################
dbbackup: ## Backup database
./manage.sh dbbackup
dbrestore: ## Restore a database backup
./manage.sh dbrestore
##############################################################################
run-docker-dev-server: ## Start docker containers with current dev source code
rm -Rf dist/
poetry build
rm -Rf deployment/dist/
cp -ruv dist deployment/
cd deployment && make down
cd deployment && ./compose.dev.sh pull
cd deployment && ./compose.dev.sh build --pull
cd deployment && ./compose.dev.sh up
shell_docker-dev-server: ## Go into bash shell in Django dev container
cd deployment && ./compose.dev.sh exec django /bin/bash
##############################################################################
.PHONY: help install lint fix test publish

Wyświetl plik

@ -54,60 +54,29 @@ This README contains only the information about local development installation.
Read [[https://github.com/jedie/PyInventory/tree/master/deployment#readme|/deployment/README]] for instruction to install PyInventory on a root server.
=== prepare
{{{
~$ git clone https://github.com/jedie/PyInventory.git
~$ cd PyInventory/
~/PyInventory$ make
_________________________________________________________________
PyInventory - *dev* Makefile
install-poetry install or update poetry
install install PyInventory via poetry
manage-update Collectstatic + makemigration + migrate
update update the sources and installation
lint Run code formatters and linter
fix-code-style Fix code formatting
tox-listenvs List all tox test environments
tox Run pytest via tox with all environments
tox-py36 Run pytest via tox with *python v3.6*
tox-py37 Run pytest via tox with *python v3.7*
tox-py38 Run pytest via tox with *python v3.8*
pytest Run pytest
update-rst-readme update README.rst from README.creole
publish Release new version to PyPi
run-dev-server Run the django dev server in endless loop.
createsuperuser Create super user
messages Make and compile locales message files
dbbackup Backup database
dbrestore Restore a database backup
run-docker-dev-server Start docker containers with current dev source code
}}}
=== local development installation
e.g.:
{{{
# install or update Poetry:
~/PyInventory$ make install-poetry
# install PyInventory via poetry:
~/PyInventory$ make install
...
# Collectstatic + makemigration + migrate:
~/PyInventory$ make manage-update
# Create a django super user:
~/PyInventory$ ./manage.sh createsuperuser
# start local dev. web server:
~/PyInventory$ make run-dev-server
# Clone project (Use your fork SSH url!):
~$ git clone https://github.com/jedie/PyInventory.git
~$ cd PyInventory
~/PyInventory$ ./devshell.py
}}}
Helpful for writing and debugging unittests is to run a local test server.
e.g.:
{{{
~/PyInventory$ ./devshell.py run_testserver
}}}
The web page is available via: {{{http://127.0.0.1:8000/}}}
Call manage commands from test project, e.g.:
{{{
~/PyInventory$ ./devshell.py manage --help
}}}
=== local docker dev run

Wyświetl plik

@ -90,61 +90,33 @@ This README contains only the information about local development installation.
Read `/deployment/README <https://github.com/jedie/PyInventory/tree/master/deployment#readme>`_ for instruction to install PyInventory on a root server.
prepare
=======
::
~$ git clone https://github.com/jedie/PyInventory.git
~$ cd PyInventory/
~/PyInventory$ make
_________________________________________________________________
PyInventory - *dev* Makefile
install-poetry install or update poetry
install install PyInventory via poetry
manage-update Collectstatic + makemigration + migrate
update update the sources and installation
lint Run code formatters and linter
fix-code-style Fix code formatting
tox-listenvs List all tox test environments
tox Run pytest via tox with all environments
tox-py36 Run pytest via tox with *python v3.6*
tox-py37 Run pytest via tox with *python v3.7*
tox-py38 Run pytest via tox with *python v3.8*
pytest Run pytest
update-rst-readme update README.rst from README.creole
publish Release new version to PyPi
run-dev-server Run the django dev server in endless loop.
createsuperuser Create super user
messages Make and compile locales message files
dbbackup Backup database
dbrestore Restore a database backup
run-docker-dev-server Start docker containers with current dev source code
local development installation
==============================
e.g.:
::
# install or update Poetry:
~/PyInventory$ make install-poetry
# install PyInventory via poetry:
~/PyInventory$ make install
...
# Collectstatic + makemigration + migrate:
~/PyInventory$ make manage-update
# Create a django super user:
~/PyInventory$ ./manage.sh createsuperuser
# start local dev. web server:
~/PyInventory$ make run-dev-server
# Clone project (Use your fork SSH url!):
~$ git clone https://github.com/jedie/PyInventory.git
~$ cd PyInventory
~/PyInventory$ ./devshell.py
Helpful for writing and debugging unittests is to run a local test server.
e.g.:
::
~/PyInventory$ ./devshell.py run_testserver
The web page is available via: ``http://127.0.0.1:8000/``
Call manage commands from test project, e.g.:
::
~/PyInventory$ ./devshell.py manage --help
local docker dev run
====================
@ -244,17 +216,17 @@ Files are separated into: "/src/" and "/development/"
history
-------
* `compare v0.8.4...master <https://github.com/jedie/PyInventory/compare/v0.8.4...master>`_ **dev**
* `compare v0.8.4...master <https://github.com/jedie/PyInventory/compare/v0.8.4...master>`_ **dev**
* tbc
* `v0.8.4 - 19.01.2021 <https://github.com/jedie/PyInventory/compare/v0.8.3...v0.8.4>`_
* `v0.8.4 - 19.01.2021 <https://github.com/jedie/PyInventory/compare/v0.8.3...v0.8.4>`_
* Search items in change list by "kind" and "tags", too
* update requirements
* `v0.8.3 - 29.12.2020 <https://github.com/jedie/PyInventory/compare/v0.8.2...v0.8.3>`_
* `v0.8.3 - 29.12.2020 <https://github.com/jedie/PyInventory/compare/v0.8.2...v0.8.3>`_
* update requirements
@ -262,11 +234,11 @@ history
* Small project setup changes
* `v0.8.2 - 20.12.2020 <https://github.com/jedie/PyInventory/compare/v0.8.1...v0.8.2>`_
* `v0.8.2 - 20.12.2020 <https://github.com/jedie/PyInventory/compare/v0.8.1...v0.8.2>`_
* Bugfix `#33 <https://github.com/jedie/PyInventory/issues/33>`_: Upload images to new created Items
* `v0.8.1 - 09.12.2020 <https://github.com/jedie/PyInventory/compare/v0.8.0...v0.8.1>`_
* `v0.8.1 - 09.12.2020 <https://github.com/jedie/PyInventory/compare/v0.8.0...v0.8.1>`_
* Fix migration: Don't create "/media/migrate.log" if there is nothing to migrate
@ -276,11 +248,11 @@ history
* update requirements
* `v0.8.0 - 06.12.2020 <https://github.com/jedie/PyInventory/compare/v0.7.0...v0.8.0>`_
* `v0.8.0 - 06.12.2020 <https://github.com/jedie/PyInventory/compare/v0.7.0...v0.8.0>`_
* Outsource the "MEDIA file serve" part into `django.tools.serve_media_app <https://github.com/jedie/django-tools/tree/master/django_tools/serve_media_app#readme>`_
* `v0.7.0 - 23.11.2020 <https://github.com/jedie/PyInventory/compare/v0.6.0...v0.7.0>`_
* `v0.7.0 - 23.11.2020 <https://github.com/jedie/PyInventory/compare/v0.6.0...v0.7.0>`_
* Change deployment setup:
@ -294,15 +266,15 @@ history
* pull all docker images before build
* `v0.6.0 - 15.11.2020 <https://github.com/jedie/PyInventory/compare/v0.5.0...v0.6.0>`_
* `v0.6.0 - 15.11.2020 <https://github.com/jedie/PyInventory/compare/v0.5.0...v0.6.0>`_
* User can store images to every item: The image can only be accessed by the same user.
* `v0.5.0 - 14.11.2020 <https://github.com/jedie/PyInventory/compare/v0.4.2...v0.5.0>`_
* `v0.5.0 - 14.11.2020 <https://github.com/jedie/PyInventory/compare/v0.4.2...v0.5.0>`_
* Merge separate git branches into one: "/src/" and "/development/" `#19 <https://github.com/jedie/PyInventory/issues/19>`_
* `v0.4.2 - 13.11.2020 <https://github.com/jedie/PyInventory/compare/v0.4.1...v0.4.2>`_
* `v0.4.2 - 13.11.2020 <https://github.com/jedie/PyInventory/compare/v0.4.1...v0.4.2>`_
* Serve static files by Caddy
@ -310,11 +282,11 @@ history
* reduce CKEditor plugins
* `v0.4.1 - 2.11.2020 <https://github.com/jedie/PyInventory/compare/v0.4.0...v0.4.1>`_
* `v0.4.1 - 2.11.2020 <https://github.com/jedie/PyInventory/compare/v0.4.0...v0.4.1>`_
* Small bugfixes
* `v0.4.0 - 1.11.2020 <https://github.com/jedie/PyInventory/compare/v0.3.2...v0.4.0>`_
* `v0.4.0 - 1.11.2020 <https://github.com/jedie/PyInventory/compare/v0.3.2...v0.4.0>`_
* Move docker stuff and production use information into separate git branch
@ -322,11 +294,11 @@ history
* Add django-processinfo: collect information about the running server processes
* `v0.3.2 - 26.10.2020 <https://github.com/jedie/PyInventory/compare/v0.3.0...v0.3.2>`_
* `v0.3.2 - 26.10.2020 <https://github.com/jedie/PyInventory/compare/v0.3.0...v0.3.2>`_
* Bugfix missing translations
* `v0.3.0 - 26.10.2020 <https://github.com/jedie/PyInventory/compare/v0.2.0...v0.3.0>`_
* `v0.3.0 - 26.10.2020 <https://github.com/jedie/PyInventory/compare/v0.2.0...v0.3.0>`_
* setup production usage:
@ -344,7 +316,7 @@ history
* Bugfix for using manage commands ``dumpdata`` and ``loaddata``
* `v0.2.0 - 24.10.2020 <https://github.com/jedie/PyInventory/compare/v0.1.0...v0.2.0>`_
* `v0.2.0 - 24.10.2020 <https://github.com/jedie/PyInventory/compare/v0.1.0...v0.2.0>`_
* Simplify item change list by nested item
@ -356,7 +328,7 @@ history
* Add docker-compose usage
* `v0.1.0 - 17.10.2020 <https://github.com/jedie/PyInventory/compare/v0.0.1...v0.1.0>`_
* `v0.1.0 - 17.10.2020 <https://github.com/jedie/PyInventory/compare/v0.0.1...v0.1.0>`_
* Enhance models, admin and finish project setup
@ -399,4 +371,4 @@ donation
------------
``Note: this file is generated from README.creole 2021-01-19 19:10:25 with "python-creole"``
``Note: this file is generated from README.creole 2021-04-05 20:00:43 with "python-creole"``

Wyświetl plik

@ -1,42 +0,0 @@
"""
Helpter
"""
from pathlib import Path
import ckeditor
ckeditor_path = Path(ckeditor.__file__).parent
print('Django-CKEditor path:', ckeditor_path)
build_config_path = Path(ckeditor_path, 'static/ckeditor/ckeditor/build-config.js')
print('Build config:', build_config_path)
plugins_path = Path(ckeditor_path, 'static/ckeditor/ckeditor/plugins')
print('Plugin path:', plugins_path)
assert plugins_path.is_dir()
plugins = {item.name for item in plugins_path.iterdir() if item.is_dir()}
in_plugins = False
with build_config_path.open('r') as f:
for line in f:
line = line.strip()
if not line:
continue
if line == 'plugins : {':
in_plugins = True
continue
if in_plugins:
if line == '},':
break
plugin_name = line.split(':', 1)[0].strip(" '")
plugins.add(plugin_name)
print("'removePlugins': (")
for plugin_name in sorted(plugins):
print(f" '{plugin_name}',")
print(')')

Wyświetl plik

@ -1,59 +0,0 @@
"""
Auto fill "verbose_name" translations:
Just copy the model field name as translation.
"""
from pathlib import Path
BASE_PATH = Path(__file__).parent.parent
MESSAGE_MAP = {
'id': 'ID',
}
def fill(po_file_path):
old_content = []
new_content = []
with po_file_path.open('r') as f:
for line in f:
old_content.append(line)
if line.startswith('msgid "'):
msgstr = ''
msgid = line[7:-2]
try:
model, attribute, kind = msgid.strip().split('.')
except ValueError:
pass
else:
if kind == 'verbose_name':
if attribute in MESSAGE_MAP:
msgstr = MESSAGE_MAP[attribute]
else:
words = attribute.replace('_', ' ').split(' ')
msgstr = ' '.join(i.capitalize() for i in words)
elif kind == 'help_text':
msgstr = ' ' # "hide" empty "help_text"
elif (line == 'msgstr ""\n' or line == 'msgstr "&nbsp;"\n') and msgstr:
line = f'msgstr "{msgstr}"\n'
line = line.replace('Content Tonie', 'Content-Tonie')
new_content.append(line)
if new_content == old_content:
print('Nothing to do, ok.')
return
with po_file_path.open('w') as f:
f.write(''.join(new_content))
print(f'updated: {po_file_path}')
if __name__ == '__main__':
for dir in ('de', 'en'):
print('_' * 100)
print(dir)
fill(Path(BASE_PATH, f'inventory/locale/{dir}/LC_MESSAGES/django.po'))

140
devshell.py 100755
Wyświetl plik

@ -0,0 +1,140 @@
#!/usr/bin/env python3
"""
developer shell
~~~~~~~~~~~~~~~
Just call this file, and the magic happens ;)
"""
import argparse
import hashlib
import signal
import subprocess
import sys
import venv
from pathlib import Path
try:
import ensurepip # noqa
except ModuleNotFoundError as err:
print(err)
print('-' * 100)
print('Error: Pip not available!')
print('Hint: "apt-get install python3-venv"\n')
raise
assert sys.version_info >= (3, 7), 'Python version is too old!'
if sys.platform == 'win32': # wtf
# Files under Windows, e.g.: .../.venv/Scripts/python3.exe
BIN_NAME = 'Scripts'
FILE_EXT = '.exe'
else:
# Files under Linux/Mac and all other than Windows, e.g.: .../.venv/bin/python3
BIN_NAME = 'bin'
FILE_EXT = ''
VENV_PATH = Path('.venv')
BIN_PATH = VENV_PATH / BIN_NAME
PYTHON_PATH = BIN_PATH / f'python3{FILE_EXT}'
PIP_PATH = BIN_PATH / f'pip{FILE_EXT}'
POETRY_PATH = BIN_PATH / f'poetry{FILE_EXT}'
DEP_LOCK_PATH = Path('poetry.lock')
DEP_HASH_PATH = VENV_PATH / '.dep_hash'
# script file defined in pyproject.toml as [tool.poetry.scripts]
# (Under Windows: ".exe" not added!)
PROJECT_SHELL_SCRIPT = BIN_PATH / 'devshell'
def get_dep_hash():
""" Get SHA512 hash from poetry.lock content. """
return hashlib.sha512(DEP_LOCK_PATH.read_bytes()).hexdigest()
def store_dep_hash():
""" Generate /.venv/.dep_hash """
DEP_HASH_PATH.write_text(get_dep_hash())
def venv_up2date():
""" Is existing .venv is up-to-date? """
if DEP_HASH_PATH.is_file():
return DEP_HASH_PATH.read_text() == get_dep_hash()
return False
def verbose_check_call(*popen_args):
popen_args = [str(arg) for arg in popen_args] # e.g.: Path() -> str for python 3.7
print(f'\n+ {" ".join(popen_args)}\n')
return subprocess.check_call(popen_args)
def noop_signal_handler(signal_num, frame):
"""
Signal handler that does nothing: Used to ignore "Ctrl-C" signals
"""
pass
def main(argv):
if len(argv) == 2 and argv[1] in ('--update', '--help'):
parser = argparse.ArgumentParser(
prog=Path(__file__).name,
description='Developer shell',
epilog='...live long and prosper...'
)
parser.add_argument(
'--update', default=False, action='store_true',
help='Force create/upgrade virtual environment'
)
parser.add_argument(
'command_args',
nargs=argparse.ZERO_OR_MORE,
help='arguments to pass to dev-setup shell/cli',
)
options = parser.parse_args(argv)
force_update = options.update
extra_args = argv[2:]
else:
force_update = False
extra_args = argv[1:]
# Create virtual env in ".../.venv/":
if not PYTHON_PATH.is_file() or force_update:
print('Create virtual env here:', VENV_PATH.absolute())
builder = venv.EnvBuilder(symlinks=True, upgrade=True, with_pip=True)
builder.create(env_dir=VENV_PATH)
# install/update "pip" and "poetry":
if not POETRY_PATH.is_file() or force_update:
# Note: Under Windows pip.exe can't replace this own .exe file, so use the module way:
verbose_check_call(PYTHON_PATH, '-m', 'pip', 'install', '-U', 'pip')
verbose_check_call(PIP_PATH, 'install', 'poetry')
# install via poetry, if:
# 1. .venv not exists
# 2. "--update" used
# 3. poetry.lock file was changed
if not PROJECT_SHELL_SCRIPT.is_file() or force_update or not venv_up2date():
verbose_check_call(POETRY_PATH, 'install')
store_dep_hash()
# The cmd2 shell should not abort on Ctrl-C => ignore "Interrupt from keyboard" signal:
signal.signal(signal.SIGINT, noop_signal_handler)
# Run project cmd shell via "setup.py" entrypoint:
# (Call it via python, because Windows sucks calling the file direct)
try:
verbose_check_call(PYTHON_PATH, PROJECT_SHELL_SCRIPT, *extra_args)
except subprocess.CalledProcessError as err:
sys.exit(err.returncode)
if __name__ == '__main__':
main(sys.argv)

727
poetry.lock wygenerowano

Plik diff jest za duży Load Diff

Wyświetl plik

@ -3,6 +3,7 @@ name = "PyInventory"
version = "0.8.4"
description = "Web based management to catalog things including state and location etc. using Python/Django."
authors = ["JensDiemer <git@jensdiemer.de>"]
homepage = "https://github.com/jedie/PyInventory"
packages = [
{ include = "inventory", from = "src" },
{ include = "inventory_project", from = "src" },
@ -60,11 +61,12 @@ docker = ["docker-compose"]
postgres = ["psycopg2-binary"]
[tool.poetry.dev-dependencies]
dev_shell = "*" # https://github.com/jedie/dev-shell
poetry-publish = "*" # https://github.com/jedie/poetry-publish
python-creole = "*" # https://github.com/jedie/python-creole
tox = "*"
pytest = "*"
pytest-randomly = "*"
pytest-randomly = "!=3.6.0" # https://github.com/python-poetry/poetry/issues/2372#issuecomment-812558146
pytest-cov = "*"
pytest-django = "*"
pytest-parallel = "*"
@ -78,9 +80,9 @@ pyupgrade = "*"
model_bakery = "*" # https://github.com/model-bakers/model_bakery
[tool.poetry.scripts]
manage = "inventory_project.manage:main"
update_rst_readme = "inventory_project.publish:update_readme"
publish = "inventory_project.publish:publish"
devshell = 'inventory_project.dev_shell:devshell_cmdloop'
run_testserver = 'inventory_project.manage:start_test_server'
[build-system]
requires = ["poetry>=0.12"]
@ -98,7 +100,7 @@ exclude="*/htmlcov/*,*/migrations/*,*/volumes/*"
atomic=true
line_length=120
case_sensitive=false
skip_glob=["*/htmlcov/*","*/migrations/*","*/volumes/*"]
skip_glob=[".*", "*/htmlcov/*","*/migrations/*","*/volumes/*"]
multi_line_output=3
include_trailing_comma=true
known_first_party=["inventory","inventory_project","inventory_tests"]
@ -108,6 +110,10 @@ sections=["FUTURE","STDLIB","THIRDPARTY","FIRSTPARTY","LOCALFOLDER"]
lines_after_imports=2
[tool.coverage.run]
omit = [".*"]
[tool.pytest.ini_options]
# https://docs.pytest.org/en/latest/customize.html#pyproject-toml
minversion = "6.0"

Wyświetl plik

@ -0,0 +1,26 @@
import os
from django.core.management import BaseCommand, call_command
class Command(BaseCommand):
help = "Run Django dev. Server"
def verbose_call(self, command, **kwargs):
self.stdout.write("\n")
self.stdout.write("_" * 79)
self.stdout.write(self.style.NOTICE(f" *** call '{command}' command:"))
self.stdout.write("\n")
call_command(command, **kwargs)
def handle(self, *args, **options):
"""
INFO: The django reloader will call this multiple times!
We check RUN_MAIN, that will be set in django.utils.autoreload
So we can skip the first migrate run.
"""
if os.environ.get("RUN_MAIN"):
self.verbose_call("migrate", run_syncdb=True, interactive=False, verbosity=1)
self.verbose_call("showmigrations", verbosity=1)
self.verbose_call("runserver", use_threading=True, use_reloader=True, verbosity=2)

Wyświetl plik

@ -1,15 +0,0 @@
"""
Just print version line on every call from commandline ;)
"""
import os
import sys
from django import __version__ as django_version
from inventory import __version__
if __name__ == 'inventory_project':
if '--version' not in sys.argv:
print(f'PyInventory v{__version__} (Django v{django_version})', file=sys.stderr)
print(f'DJANGO_SETTINGS_MODULE={os.environ["DJANGO_SETTINGS_MODULE"]!r}', file=sys.stderr)

Wyświetl plik

@ -0,0 +1,228 @@
import sys
from pathlib import Path
import cmd2
from creole.setup_utils import assert_rst_readme, update_rst_readme
from dev_shell.base_cmd2_app import DevShellBaseApp
from dev_shell.command_sets import DevShellBaseCommandSet
from dev_shell.command_sets.dev_shell_commands import DevShellCommandSet as OriginDevShellCommandSet
from dev_shell.command_sets.dev_shell_commands import run_linters
from dev_shell.config import DevShellConfig
from dev_shell.utils.assertion import assert_is_dir
from dev_shell.utils.subprocess_utils import verbose_check_call
from poetry_publish.publish import poetry_publish
import inventory
PACKAGE_ROOT = Path(inventory.__file__).parent.parent.parent
assert_is_dir(PACKAGE_ROOT / 'src' / 'inventory')
def call_manage_py(*args):
verbose_check_call('python3', '-m', 'inventory_project.manage.main', *args)
@cmd2.with_default_category('PyInventory commands')
class PyInventoryCommandSet(DevShellBaseCommandSet):
def do_manage(self, statement: cmd2.Statement):
"""
Call PyInventory test "manage.py"
"""
call_manage_py(*statement.arg_list)
def do_run_testserver(self, statement: cmd2.Statement):
"""
Start Django dev server with the test project
"""
# Start the "[tool.poetry.scripts]" script via subprocess
# This works good with django dev server reloads
verbose_check_call('run_testserver', *statement.arg_list)
def do_makemessages(self, statement: cmd2.Statement):
"""
Make and compile locales message files
"""
call_manage_py(
'makemessages',
'--all',
'--no-location', '--no-obsolete',
'--ignore=htmlcov', '--ignore=.*'
)
call_manage_py(
'compilemessages',
'--ignore=htmlcov', '--ignore=.*'
)
def do_update_rst_readme(self, statement: cmd2.Statement):
"""
update README.rst from README.creole
"""
update_rst_readme(
package_root=PACKAGE_ROOT,
filename='README.creole'
)
def do_ckeditor_info(self, statement: cmd2.Statement):
"""
Print some information about CKEditor
"""
import ckeditor
ckeditor_path = Path(ckeditor.__file__).parent
print('Django-CKEditor path:', ckeditor_path)
build_config_path = Path(ckeditor_path, 'static/ckeditor/ckeditor/build-config.js')
print('Build config:', build_config_path)
plugins_path = Path(ckeditor_path, 'static/ckeditor/ckeditor/plugins')
print('Plugin path:', plugins_path)
assert plugins_path.is_dir()
plugins = {item.name for item in plugins_path.iterdir() if item.is_dir()}
in_plugins = False
with build_config_path.open('r') as f:
for line in f:
line = line.strip()
if not line:
continue
if line == 'plugins : {':
in_plugins = True
continue
if in_plugins:
if line == '},':
break
plugin_name = line.split(':', 1)[0].strip(" '")
plugins.add(plugin_name)
print("'removePlugins': (")
for plugin_name in sorted(plugins):
print(f" '{plugin_name}',")
print(')')
def do_fill_verbose_name_translations(self, statement: cmd2.Statement):
"""
Auto fill "verbose_name" translations:
Just copy the model field name as translation.
"""
MESSAGE_MAP = {'id': 'ID'}
for lang_code in ('de', 'en'):
print('_' * 100)
print(lang_code)
po_file_path = PACKAGE_ROOT / f'src/inventory/locale/{lang_code}/LC_MESSAGES/django.po'
old_content = []
new_content = []
with po_file_path.open('r') as f:
for line in f:
old_content.append(line)
if line.startswith('msgid "'):
msgstr = ''
msgid = line[7:-2]
try:
model, attribute, kind = msgid.strip().split('.')
except ValueError:
pass
else:
if kind == 'verbose_name':
if attribute in MESSAGE_MAP:
msgstr = MESSAGE_MAP[attribute]
else:
words = attribute.replace('_', ' ').split(' ')
msgstr = ' '.join(i.capitalize() for i in words)
elif kind == 'help_text':
msgstr = ' ' # "hide" empty "help_text"
elif (line == 'msgstr ""\n' or line == 'msgstr "&nbsp;"\n') and msgstr:
line = f'msgstr "{msgstr}"\n'
line = line.replace('Content Tonie', 'Content-Tonie')
new_content.append(line)
if new_content == old_content:
print('Nothing to do, ok.')
return
with po_file_path.open('w') as f:
f.write(''.join(new_content))
print(f'updated: {po_file_path}')
class DevShellCommandSet(OriginDevShellCommandSet):
# TODO:
# pyupgrade --exit-zero-even-if-changed --py3-plus --py36-plus --py37-plus --py38-plus
# `find . -name "*.py" -type f ! -path "./.tox/*" ! -path "./htmlcov/*" ! -path "*/volumes/*"
def do_publish(self, statement: cmd2.Statement):
"""
Publish "dev-shell" to PyPi
"""
# don't publish if README is not up-to-date:
assert_rst_readme(package_root=PACKAGE_ROOT, filename='README.creole')
# don't publish if code style wrong:
run_linters()
# don't publish if test fails:
verbose_check_call('pytest', '-x')
poetry_publish(
package_root=PACKAGE_ROOT,
version=inventory.__version__,
creole_readme=True # don't publish if README.rst is not up-to-date
)
def do_linting(self, statement: cmd2.Statement):
"""
Linting: Check code style with flake8, isort and flynt
"""
verbose_check_call('flake8', exit_on_error=True)
verbose_check_call(
'isort', '--check-only', '.',
exit_on_error=True
)
verbose_check_call(
'flynt', '--fail-on-change', '--line_length=119', 'src',
exit_on_error=True
)
class DevShellApp(DevShellBaseApp):
pass
def get_devshell_app_kwargs():
"""
Generate the kwargs for the cmd2 App.
(Separated because we needs the same kwargs in tests)
"""
config = DevShellConfig(package_module=inventory)
# initialize all CommandSet() with context:
kwargs = dict(
config=config
)
app_kwargs = dict(
config=config,
command_sets=[
PyInventoryCommandSet(**kwargs),
DevShellCommandSet(**kwargs),
]
)
return app_kwargs
def devshell_cmdloop():
"""
Entry point to start the "dev-shell" cmd2 app.
Used in: [tool.poetry.scripts]
"""
c = DevShellApp(**get_devshell_app_kwargs())
sys.exit(c.cmdloop())

Wyświetl plik

@ -3,17 +3,21 @@ import os
import sys
from pathlib import Path
from django import __version__ as django_version
BASE_PATH = Path(__file__).parent
import inventory
from inventory import __version__
BASE_PATH = Path(inventory.__file__).parent
def main():
assert 'DJANGO_SETTINGS_MODULE' in os.environ, 'No "DJANGO_SETTINGS_MODULE" in environment!'
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'inventory_project.settings.local')
# Change to /src/ and add it to sys.path
src_path = Path(BASE_PATH, 'src').resolve()
assert src_path.is_dir(), f'Path not exists: {src_path}'
sys.path.insert(0, str(src_path))
if '--version' not in sys.argv:
print(f'PyInventory v{__version__} (Django v{django_version})', file=sys.stderr)
print(f'DJANGO_SETTINGS_MODULE={os.environ["DJANGO_SETTINGS_MODULE"]!r}', file=sys.stderr)
try:
from django.core.management import execute_from_command_line
@ -31,5 +35,13 @@ def main():
raise
def start_test_server():
"""
Entrypoint for "[tool.poetry.scripts]" script started by devshell command.
"""
sys.argv = [__file__, "run_testserver"]
main()
if __name__ == '__main__':
main()

Wyświetl plik

@ -1,7 +1,9 @@
import shutil
import subprocess
import sys
from pathlib import Path
from dev_shell.utils.assertion import assert_is_file
import inventory
@ -9,7 +11,6 @@ BASE_PATH = Path(inventory.__file__).parent.parent.parent
def test_lint():
assert Path(BASE_PATH, 'Makefile').is_file()
make_bin = shutil.which('make')
assert make_bin is not None
subprocess.check_call([make_bin, 'lint'], cwd=BASE_PATH)
dev_shell_py = BASE_PATH / 'devshell.py'
assert_is_file(dev_shell_py)
subprocess.check_call([sys.executable, str(dev_shell_py), 'linting'])