diff --git a/.gitignore b/.gitignore index e69de29..b882ca2 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,2 @@ +.mypy_cache/ +.pytest_cache/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..488b507 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: minimal + +services: docker + +install: + - travis_retry make build + +script: + - make all-tests + +if: | + branch = master OR \ + branch =~ /^[0-9\.]+$/ OR \ + tag IS present OR \ + type = pull_request diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index c1e3c94..8acc933 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,4 +1,3 @@ -Code of conduct -=============== +# Code of conduct The Libero community follows a [code of conduct](https://libero.pub/code-of-conduct). diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8381c62 --- /dev/null +++ b/Makefile @@ -0,0 +1,87 @@ +PROJECT_CODEBASE_DIR = "search/" +TESTS_DIR = "tests/" +FILES_TO_CHECK = $(PROJECT_CODEBASE_DIR) $(TESTS_DIR) +DOCKER_COMPOSE = docker-compose -f docker/docker-compose.dev.yml + +help: + @echo "start - builds and/or starts all services" + @echo "stop - stops all running containers belonging to the project" + @echo "checks - runs static checks such as linting without running unit tests" + @echo "tests - runs unit tests in debug mode (able to use pdb breakpoints)" + @echo "all-tests - runs static checks and unit tests" + @echo "fix-imports - silently modifies imports on all project files that do not" + @echo " adhere to project coding standards regarding imports" + @echo "run - only runs the application service (able to use pdb breakpoints)" + @echo "shell - enter the shell of the application service" + @echo "build - builds the application service" + @echo "---------------------------------------------------------------" + @echo "make build should be run after adding and removing dependencies" + @echo "---------------------------------------------------------------" + @echo "dependency-tree - show dependency tree" + @echo "d-tree - alias for \"dependency-tree\"" + @echo "add-dependency - add or update a python dependency by specifying a package" + @echo " e.g. make dependency package=flask" + @echo " e.g. make dependency package=flask==1.0.2" + @echo "add-dev-dependency - same as \"dependency\" except the package will be installed" + @echo " during development only" + @echo "remove-dependency - remove a python dependency by specifying a package" + @echo " e.g. make remove-dependency package=flask" + @echo "remove-dev-dependency - same as \"remove-dependency\" regarding development dependencies" + +start: + $(DOCKER_COMPOSE) up + +stop: + $(DOCKER_COMPOSE) down -v + +checks: + $(DOCKER_COMPOSE) run --rm web /bin/bash -c "\ + echo \"Running checks...\" && \ + echo \"- check for breakpoints\" && \ + source scripts/find-breakpoints.sh && \ + echo \"- mypy\" && \ + mypy $(FILES_TO_CHECK) && \ + echo \"- flake8\" && \ + flake8 $(FILES_TO_CHECK) && \ + echo \"- pylint\" && \ + pylint --rcfile=setup.cfg $(FILES_TO_CHECK) && \ + echo \"All checks completed\" \ + " + +.PHONY: tests +tests: + $(DOCKER_COMPOSE) run --rm --service-ports web /bin/bash -c \ + "pytest -s --pdbcls=IPython.terminal.debugger:Pdb -vv" + +all-tests: checks tests + +fix-imports: + $(DOCKER_COMPOSE) run --rm web /bin/bash -c "isort -y" + +.PHONY: run +run: + $(DOCKER_COMPOSE) run --rm --service-ports web + +shell: + $(DOCKER_COMPOSE) run --rm --service-ports web /bin/bash + +build: + $(DOCKER_COMPOSE) build web + +dependency-tree: + $(DOCKER_COMPOSE) run --rm web /bin/bash -c "poetry show --tree" + +d-tree: dependency-tree # alias for dependency-tree + +add-dependency: + $(DOCKER_COMPOSE) run --rm web /bin/bash -c "poetry add $(package)" + +add-dev-dependency: + $(DOCKER_COMPOSE) run --rm web /bin/bash -c "poetry add $(package) --dev" + +remove-dependency: + $(DOCKER_COMPOSE) run --rm web /bin/bash -c "poetry remove $(package)" + +remove-dev-dependency: + $(DOCKER_COMPOSE) run --rm web /bin/bash -c "poetry remove $(package) --dev" + diff --git a/README.md b/README.md index 6c403cb..2d94092 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,84 @@ -Libero search -============= +# Libero Search +This project is an implementation of Libero search API -Getting help ------------- +## Dependencies + +* [Docker](https://www.docker.com/) + +## Getting started +This project provides a `Makefile` with short commands to run common tasks. +Typically, MacOS and most Linux distributions come with [gnu make](https://www.gnu.org/software/make/) +installed. If are unable to run the commands below because your system doesn't +have `gnu make` installed, you can try to install it or copy and paste commands +found in the `Makefile` into your command line interface. + +* `make help` for a full list of commands. +* `make start` builds and/or runs the site locally configured for development purposes. +* `make stop` stops containers and cleans up any anonymous volumes. + +## Running the tests + +* `make tests` runs unit tests. +* `make checks` runs static checks such as: mypy, flake8, pylint. +* `make all-tests` combines the two commands above by running all checks followed + by unit tests + +## Adding and removing dependencies + +The following commands update files that keep track of project dependencies. + +```bash +make add-dependency package= +make add-dev-dependency package= + +# examples: +make add-dependency package=flask +make add-dependency package=flask==1.0.2 +make add-dev-dependency package=pytest +``` +These commands add a python package as a project dependency or +development dependency alike. You can specify just the package name to get the +latest version or specify a specific version number. + +```bash +make remove-dependency package= +make remove-dev-dependency package= +``` +These commands remove dependencies accordingly. + +```bash +make build +``` +Will rebuild the python container and persist changes made to dependency files. + +## Other useful commands +```bash +make run +``` +Use this command to only run the python service. +It is possible to use pdb breakpoints with this configuration. + +```bash +make shell +``` +Use this to run the python container that would run the application and enter a +bash prompt. + +```bash +make dependency-tree +# or +make d-tree +``` +Use this to display a dependency tree + +```bash +make fix-imports +``` +When checks are run warnings are displayed because imports do not follow the +project conventions as specified in `setup.cfg` under `[isort]`. +Use this command to automatically fix imports for all project python files. + +## Getting help - Report a bug or request a feature on [GitHub](https://github.com/libero/libero/issues/new/choose). - Ask a question on the [Libero Community Slack](https://libero.pub/join-slack). diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev new file mode 100644 index 0000000..196e2e1 --- /dev/null +++ b/docker/Dockerfile.dev @@ -0,0 +1,26 @@ +FROM python:3.7.2-alpine3.8 + +ENV PYTHONUNBUFFERED=1 +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PATH "/root/.poetry/bin:$PATH" + +WORKDIR /srv/app + +RUN apk add --no-cache \ + bash \ + grep \ + libxslt-dev + +RUN apk add --no-cache --virtual .build-dependencies curl && \ + curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python && \ + poetry config settings.virtualenvs.create false && \ + apk del .build-dependencies + +COPY ./pyproject.toml ./poetry.lock ./ + +RUN apk add --no-cache --virtual .build-dependencies \ + gcc \ + musl-dev \ + && \ + poetry install && \ + apk del .build-dependencies diff --git a/docker/dev.env b/docker/dev.env new file mode 100644 index 0000000..b22cbc9 --- /dev/null +++ b/docker/dev.env @@ -0,0 +1,2 @@ +FLASK_APP=search +FLASK_ENV=development diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml new file mode 100644 index 0000000..09833db --- /dev/null +++ b/docker/docker-compose.dev.yml @@ -0,0 +1,15 @@ +version: '3' + +services: + web: + build: + context: .. + dockerfile: docker/Dockerfile.dev + working_dir: /srv/app + volumes: + - ..:/srv/app + command: "flask run --host=0.0.0.0" + ports: + - "5000:5000" + env_file: + - dev.env diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..03a6729 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,499 @@ +[[package]] +category = "dev" +description = "Disable App Nap on OS X 10.9" +marker = "python_version >= \"3.3\" and sys_platform == \"darwin\"" +name = "appnope" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "dev" +description = "An abstract syntax tree for Python with inference support." +name = "astroid" +optional = false +python-versions = ">=3.4.*" +version = "2.1.0" + +[package.dependencies] +lazy-object-proxy = "*" +six = "*" +wrapt = "*" + +[[package]] +category = "dev" +description = "Atomic file writes." +name = "atomicwrites" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.2.1" + +[[package]] +category = "dev" +description = "Classes Without Boilerplate" +name = "attrs" +optional = false +python-versions = "*" +version = "18.2.0" + +[[package]] +category = "dev" +description = "Specifications for callback functions passed in to an API" +marker = "python_version >= \"3.3\"" +name = "backcall" +optional = false +python-versions = "*" +version = "0.1.0" + +[[package]] +category = "main" +description = "Composable command line interface toolkit" +name = "click" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "7.0" + +[[package]] +category = "dev" +description = "Cross-platform colored terminal text." +marker = "python_version >= \"3.3\" and sys_platform == \"win32\" or sys_platform == \"win32\"" +name = "colorama" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.4.1" + +[[package]] +category = "dev" +description = "Better living through Python with decorators" +marker = "python_version >= \"3.3\"" +name = "decorator" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "4.3.2" + +[[package]] +category = "dev" +description = "the modular source code checker: pep8, pyflakes and co" +name = "flake8" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "3.6.0" + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.4.0,<2.5.0" +pyflakes = ">=2.0.0,<2.1.0" +setuptools = ">=30" + +[[package]] +category = "dev" +description = "flake8 plugin that integrates isort ." +name = "flake8-isort" +optional = false +python-versions = "*" +version = "2.6.0" + +[package.dependencies] +flake8 = ">=3.2.1" +isort = ">=4.3.0" +testfixtures = "*" + +[[package]] +category = "main" +description = "A simple framework for building complex web applications." +name = "flask" +optional = false +python-versions = "*" +version = "1.0.2" + +[package.dependencies] +Jinja2 = ">=2.10" +Werkzeug = ">=0.14" +click = ">=5.1" +itsdangerous = ">=0.24" + +[[package]] +category = "dev" +description = "IPython-enabled pdb" +name = "ipdb" +optional = false +python-versions = ">=2.7" +version = "0.11" + +[package.dependencies] +setuptools = "*" + +[package.dependencies.ipython] +python = ">=3.3" +version = ">=5.0.0" + +[[package]] +category = "dev" +description = "IPython: Productive Interactive Computing" +marker = "python_version >= \"3.3\"" +name = "ipython" +optional = false +python-versions = ">=3.5" +version = "7.2.0" + +[package.dependencies] +appnope = "*" +backcall = "*" +colorama = "*" +decorator = "*" +jedi = ">=0.10" +pexpect = "*" +pickleshare = "*" +prompt-toolkit = ">=2.0.0,<2.1.0" +pygments = "*" +setuptools = ">=18.5" +traitlets = ">=4.2" + +[[package]] +category = "dev" +description = "Vestigial utilities from IPython" +marker = "python_version >= \"3.3\"" +name = "ipython-genutils" +optional = false +python-versions = "*" +version = "0.2.0" + +[[package]] +category = "dev" +description = "A Python utility / library to sort Python imports." +name = "isort" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "4.3.4" + +[[package]] +category = "main" +description = "Various helpers to pass data to untrusted environments and back." +name = "itsdangerous" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.1.0" + +[[package]] +category = "dev" +description = "An autocompletion tool for Python that can be used for text editors." +marker = "python_version >= \"3.3\"" +name = "jedi" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.13.2" + +[package.dependencies] +parso = ">=0.3.0" + +[[package]] +category = "main" +description = "A small but fast and easy to use stand-alone template engine written in pure python." +name = "jinja2" +optional = false +python-versions = "*" +version = "2.10" + +[package.dependencies] +MarkupSafe = ">=0.23" + +[[package]] +category = "dev" +description = "A fast and thorough lazy object proxy." +name = "lazy-object-proxy" +optional = false +python-versions = "*" +version = "1.3.1" + +[[package]] +category = "main" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +name = "lxml" +optional = false +python-versions = "*" +version = "4.3.0" + +[[package]] +category = "main" +description = "Safely add untrusted strings to HTML/XML markup." +name = "markupsafe" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +version = "1.1.0" + +[[package]] +category = "dev" +description = "McCabe checker, plugin for flake8" +name = "mccabe" +optional = false +python-versions = "*" +version = "0.6.1" + +[[package]] +category = "dev" +description = "More routines for operating on iterables, beyond itertools" +name = "more-itertools" +optional = false +python-versions = "*" +version = "5.0.0" + +[package.dependencies] +six = ">=1.0.0,<2.0.0" + +[[package]] +category = "dev" +description = "Optional static typing for Python" +name = "mypy" +optional = false +python-versions = "*" +version = "0.660" + +[package.dependencies] +mypy_extensions = ">=0.4.0,<0.5.0" +typed-ast = ">=1.2.0,<1.3.0" + +[[package]] +category = "dev" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +name = "mypy-extensions" +optional = false +python-versions = "*" +version = "0.4.1" + +[[package]] +category = "dev" +description = "A Python Parser" +marker = "python_version >= \"3.3\"" +name = "parso" +optional = false +python-versions = "*" +version = "0.3.2" + +[[package]] +category = "dev" +description = "Pexpect allows easy control of interactive console applications." +marker = "python_version >= \"3.3\" and sys_platform != \"win32\"" +name = "pexpect" +optional = false +python-versions = "*" +version = "4.6.0" + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +category = "dev" +description = "Tiny 'shelve'-like database with concurrency support" +marker = "python_version >= \"3.3\"" +name = "pickleshare" +optional = false +python-versions = "*" +version = "0.7.5" + +[[package]] +category = "dev" +description = "plugin and hook calling mechanisms for python" +name = "pluggy" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "0.8.1" + +[[package]] +category = "dev" +description = "Library for building powerful interactive command lines in Python" +marker = "python_version >= \"3.3\"" +name = "prompt-toolkit" +optional = false +python-versions = "*" +version = "2.0.8" + +[package.dependencies] +six = ">=1.9.0" +wcwidth = "*" + +[[package]] +category = "dev" +description = "Run a subprocess in a pseudo terminal" +marker = "python_version >= \"3.3\" and sys_platform != \"win32\"" +name = "ptyprocess" +optional = false +python-versions = "*" +version = "0.6.0" + +[[package]] +category = "dev" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +name = "py" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "1.7.0" + +[[package]] +category = "dev" +description = "Python style guide checker" +name = "pycodestyle" +optional = false +python-versions = "*" +version = "2.4.0" + +[[package]] +category = "dev" +description = "passive checker of Python programs" +name = "pyflakes" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "2.0.0" + +[[package]] +category = "dev" +description = "Pygments is a syntax highlighting package written in Python." +marker = "python_version >= \"3.3\"" +name = "pygments" +optional = false +python-versions = "*" +version = "2.3.1" + +[[package]] +category = "dev" +description = "python code static checker" +name = "pylint" +optional = false +python-versions = ">=3.4.*" +version = "2.2.2" + +[package.dependencies] +astroid = ">=2.0.0" +colorama = "*" +isort = ">=4.2.5" +mccabe = "*" + +[[package]] +category = "dev" +description = "pytest: simple powerful testing with Python" +name = "pytest" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "4.1.1" + +[package.dependencies] +atomicwrites = ">=1.0" +attrs = ">=17.4.0" +colorama = "*" +more-itertools = ">=4.0.0" +pluggy = ">=0.7" +py = ">=1.5.0" +setuptools = "*" +six = ">=1.10.0" + +[[package]] +category = "dev" +description = "Python 2 and 3 compatibility utilities" +name = "six" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*" +version = "1.12.0" + +[[package]] +category = "dev" +description = "A collection of helpers and mock objects for unit tests and doc tests." +name = "testfixtures" +optional = false +python-versions = "*" +version = "6.5.0" + +[[package]] +category = "dev" +description = "Traitlets Python config system" +marker = "python_version >= \"3.3\"" +name = "traitlets" +optional = false +python-versions = "*" +version = "4.3.2" + +[package.dependencies] +decorator = "*" +ipython-genutils = "*" +six = "*" + +[[package]] +category = "dev" +description = "a fork of Python 2 and 3 ast modules with type comment support" +name = "typed-ast" +optional = false +python-versions = "*" +version = "1.2.0" + +[[package]] +category = "dev" +description = "Measures number of Terminal column cells of wide-character codes" +marker = "python_version >= \"3.3\"" +name = "wcwidth" +optional = false +python-versions = "*" +version = "0.1.7" + +[[package]] +category = "main" +description = "The comprehensive WSGI web application library." +name = "werkzeug" +optional = false +python-versions = "*" +version = "0.14.1" + +[[package]] +category = "dev" +description = "Module for decorators, wrappers and monkey patching." +name = "wrapt" +optional = false +python-versions = "*" +version = "1.11.1" + +[metadata] +content-hash = "54fb1adcd83d476cf5950a6006cc7803c4c250f0fd9dcbe9139f64bc4ce72379" +python-versions = "^3.7" + +[metadata.hashes] +appnope = ["5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"] +astroid = ["35b032003d6a863f5dcd7ec11abd5cd5893428beaa31ab164982403bcb311f22", "6a5d668d7dc69110de01cdf7aeec69a679ef486862a0850cc0fd5571505b6b7e"] +atomicwrites = ["0312ad34fcad8fac3704d441f7b317e50af620823353ec657a53e981f92920c0", "ec9ae8adaae229e4f8446952d204a3e4b5fdd2d099f9be3aaf556120135fb3ee"] +attrs = ["10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69", "ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"] +backcall = ["38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", "bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"] +click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] +colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"] +decorator = ["33cd704aea07b4c28b3eb2c97d288a06918275dac0ecebdaf1bc8a48d98adb9e", "cabb249f4710888a2fc0e13e9a16c343d932033718ff62e1e9bc93a9d3a9122b"] +flake8 = ["6a35f5b8761f45c5513e3405f110a86bea57982c3b75b766ce7b65217abe1670", "c01f8a3963b3571a8e6bd7a4063359aff90749e160778e03817cd9b71c9e07d2"] +flake8-isort = ["3c107c405dd6e3dbdcccb2f84549d76d58a07120cd997a0560fab8b84c305f2a", "76d7dd6aec2762c608b442abebb0aaedb72fc75f9a075241a89e4784d8a39900"] +flask = ["2271c0070dbcb5275fad4a82e29f23ab92682dc45f9dfbc22c02ba9b9322ce48", "a080b744b7e345ccfcbc77954861cb05b3c63786e93f2b3875e0913d44b43f05"] +ipdb = ["7081c65ed7bfe7737f83fa4213ca8afd9617b42ff6b3f1daf9a3419839a2a00a"] +ipython = ["6a9496209b76463f1dec126ab928919aaf1f55b38beb9219af3fe202f6bbdd12", "f69932b1e806b38a7818d9a1e918e5821b685715040b48e59c657b3c7961b742"] +ipython-genutils = ["72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", "eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"] +isort = ["1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af", "b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8", "ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497"] +itsdangerous = ["321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", "b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"] +jedi = ["571702b5bd167911fe9036e5039ba67f820d6502832285cde8c881ab2b2149fd", "c8481b5e59d34a5c7c42e98f6625e633f6ef59353abea6437472c7ec2093f191"] +jinja2 = ["74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", "f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"] +lazy-object-proxy = ["0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33", "1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39", "209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019", "27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088", "27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b", "2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e", "2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6", "320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b", "50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5", "5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff", "61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd", "6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7", "7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff", "7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d", "7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2", "7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35", "81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4", "933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514", "94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252", "ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109", "bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f", "cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c", "d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92", "ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577", "e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d", "e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d", "e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f", "eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a", "f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b"] +lxml = ["0dd6589fa75d369ba06d2b5f38dae107f76ea127f212f6a7bee134f6df2d1d21", "1afbac344aa68c29e81ab56c1a9411c3663157b5aee5065b7fa030b398d4f7e0", "1baad9d073692421ad5dbbd81430aba6c7f5fdc347f03537ae046ddf2c9b2297", "1d8736421a2358becd3edf20260e41a06a0bf08a560480d3a5734a6bcbacf591", "1e1d9bddc5afaddf0de76246d3f2152f961697ad7439c559f179002682c45801", "1f179dc8b2643715f020f4d119d5529b02cd794c1c8f305868b73b8674d2a03f", "241fb7bdf97cb1df1edfa8f0bcdfd80525d4023dac4523a241907c8b2f44e541", "2f9765ee5acd3dbdcdc0d0c79309e01f7c16bc8d39b49250bf88de7b46daaf58", "312e1e1b1c3ce0c67e0b8105317323e12807955e8186872affb667dbd67971f6", "3273db1a8055ca70257fd3691c6d2c216544e1a70b673543e15cc077d8e9c730", "34dfaa8c02891f9a246b17a732ca3e99c5e42802416628e740a5d1cb2f50ff49", "3aa3f5288af349a0f3a96448ebf2e57e17332d99f4f30b02093b7948bd9f94cc", "51102e160b9d83c1cc435162d90b8e3c8c93b28d18d87b60c56522d332d26879", "56115fc2e2a4140e8994eb9585119a1ae9223b506826089a3ba753a62bd194a6", "69d83de14dbe8fe51dccfd36f88bf0b40f5debeac763edf9f8325180190eba6e", "99fdce94aeaa3ccbdfcb1e23b34273605c5853aa92ec23d84c84765178662c6c", "a7c0cd5b8a20f3093ee4a67374ccb3b8a126743b15a4d759e2a1bf098faac2b2", "abe12886554634ed95416a46701a917784cb2b4c77bfacac6916681d49bbf83d", "b4f67b5183bd5f9bafaeb76ad119e977ba570d2b0e61202f534ac9b5c33b4485", "bdd7c1658475cc1b867b36d5c4ed4bc316be8d3368abe03d348ba906a1f83b0e", "c6f24149a19f611a415a51b9bc5f17b6c2f698e0d6b41ffb3fa9f24d35d05d73", "d1e111b3ab98613115a208c1017f266478b0ab224a67bc8eac670fa0bad7d488", "d6520aa965773bbab6cb7a791d5895b00d02cf9adc93ac2bf4edb9ac1a6addc5", "dd185cde2ccad7b649593b0cda72021bc8a91667417001dbaf24cd746ecb7c11", "de2e5b0828a9d285f909b5d2e9d43f1cf6cf21fe65bc7660bdaa1780c7b58298", "f726444b8e909c4f41b4fde416e1071cf28fa84634bfb4befdf400933b6463af"] +markupsafe = ["048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432", "130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b", "19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9", "1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af", "1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834", "1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd", "1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d", "31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7", "3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b", "4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3", "525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c", "52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2", "52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7", "5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36", "5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1", "5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e", "7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1", "83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c", "857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856", "98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550", "bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492", "d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672", "e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401", "edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6", "efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6", "f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c", "f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd", "fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"] +mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] +more-itertools = ["38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4", "c0a5785b1109a6bd7fac76d6837fd1feca158e54e521ccd2ae8bfe393cc9d4fc", "fe7a7cae1ccb57d33952113ff4fa1bc5f879963600ed74918f1236e212ee50b9"] +mypy = ["986a7f97808a865405c5fd98fae5ebfa963c31520a56c783df159e9a81e41b3e", "cc5df73cc11d35655a8c364f45d07b13c8db82c000def4bd7721be13356533b4"] +mypy-extensions = ["37e0e956f41369209a3d5f34580150bcacfabaa57b33a15c0b25f4b5725e0812", "b16cabe759f55e3409a7d231ebd2841378fb0c27a5d1994719e340e4f429ac3e"] +parso = ["4b8f9ed80c3a4a3191aa3261505d868aa552dd25649cb13a7d73b6b7315edf2d", "5a120be2e8863993b597f1c0437efca799e90e0793c98ae5d4e34ebd00140e31"] +pexpect = ["2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba", "3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b"] +pickleshare = ["87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", "9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"] +pluggy = ["8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616", "980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a"] +prompt-toolkit = ["88002cc618cacfda8760c4539e76c3b3f148ecdb7035a3d422c7ecdc90c2a3ba", "c6655a12e9b08edb8cf5aeab4815fd1e1bdea4ad73d3bbf269cf2e0c4eb75d5e", "df5835fb8f417aa55e5cafadbaeb0cf630a1e824aad16989f9f0493e679ec010"] +ptyprocess = ["923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", "d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"] +py = ["bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694", "e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6"] +pycodestyle = ["74abc4e221d393ea5ce1f129ea6903209940c1ecd29e002e8c6933c2b21026e0", "cbc619d09254895b0d12c2c691e237b2e91e9b2ecf5e84c26b35400f93dcfb83", "cbfca99bd594a10f674d0cd97a3d802a1fdef635d4361e1a2658de47ed261e3a"] +pyflakes = ["9a7662ec724d0120012f6e29d6248ae3727d821bba522a0e6b356eff19126a49", "f661252913bc1dbe7fcfcbf0af0db3f42ab65aabd1a6ca68fe5d466bace94dae"] +pygments = ["5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a", "e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"] +pylint = ["689de29ae747642ab230c6d37be2b969bf75663176658851f456619aacf27492", "771467c434d0d9f081741fec1d64dfb011ed26e65e12a28fe06ca2f61c4d556c"] +pytest = ["41568ea7ecb4a68d7f63837cf65b92ce8d0105e43196ff2b26622995bb3dc4b2", "c3c573a29d7c9547fb90217ece8a8843aa0c1328a797e200290dc3d0b4b823be"] +six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] +testfixtures = ["59df1b51118978400d9926d5c1efb295f900ae626a54113323732647e453a80f", "cbd0f095d178de578709bcf4cc6eea896964635d2b41386d1cc7583674809b0e"] +traitlets = ["9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", "c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"] +typed-ast = ["023625bfa9359e29bd6e24cac2a4503495b49761d48a5f1e38333fc4ac4d93fe", "07591f7a5fdff50e2e566c4c1e9df545c75d21e27d98d18cb405727ed0ef329c", "153e526b0f4ffbfada72d0bb5ffe8574ba02803d2f3a9c605c8cf99dfedd72a2", "3ad2bdcd46a4a1518d7376e9f5016d17718a9ed3c6a3f09203d832f6c165de4a", "3ea98c84df53ada97ee1c5159bb3bc784bd734231235a1ede14c8ae0775049f7", "51a7141ccd076fa561af107cfb7a8b6d06a008d92451a1ac7e73149d18e9a827", "52c93cd10e6c24e7ac97e8615da9f224fd75c61770515cb323316c30830ddb33", "6344c84baeda3d7b33e157f0b292e4dd53d05ddb57a63f738178c01cac4635c9", "64699ca1b3bd5070bdeb043e6d43bc1d0cebe08008548f4a6bee782b0ecce032", "74903f2e56bbffe29282ef8a5487d207d10be0f8513b41aff787d954a4cf91c9", "7891710dba83c29ee2bd51ecaa82f60f6bede40271af781110c08be134207bf2", "91976c56224e26c256a0de0f76d2004ab885a29423737684b4f7ebdd2f46dde2", "9bad678a576ecc71f25eba9f1e3fd8d01c28c12a2834850b458428b3e855f062", "b4726339a4c180a8b6ad9d8b50d2b6dc247e1b79b38fe2290549c98e82e4fd15", "ba36f6aa3f8933edf94ea35826daf92cbb3ec248b89eccdc053d4a815d285357", "bbc96bde544fd19e9ef168e4dfa5c3dfe704bfa78128fa76f361d64d6b0f731a", "c0c927f1e44469056f7f2dada266c79b577da378bbde3f6d2ada726d131e4824", "c0f9a3708008aa59f560fa1bd22385e05b79b8e38e0721a15a8402b089243442", "f0bf6f36ff9c5643004171f11d2fdc745aa3953c5aacf2536a0685db9ceb3fb1", "f5be39a0146be663cbf210a4d95c3c58b2d7df7b043c9047c5448e358f0550a2", "fcd198bf19d9213e5cbf2cde2b9ef20a9856e716f76f9476157f90ae6de06cc6"] +wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"] +werkzeug = ["c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", "d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b"] +wrapt = ["4aea003270831cceb8a90ff27c4031da6ead7ec1886023b80ce0dfe0adf61533"] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..c260941 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[tool.poetry] +name = "search" +version = "0.1.0" +description = "Libero search service" +authors = [""] +license = "MIT" + +[tool.poetry.dependencies] +python = "^3.7" +flask = "^1.0" +lxml = "^4.3" + +[tool.poetry.dev-dependencies] +pytest = "^4.1" +ipdb = "^0.11.0" +flake8 = "^3.6" +flake8-isort = "^2.6" +pylint = "^2.2" +mypy = "^0.660.0" + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" diff --git a/scripts/find-breakpoints.sh b/scripts/find-breakpoints.sh new file mode 100755 index 0000000..822e427 --- /dev/null +++ b/scripts/find-breakpoints.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +OUTPUT=$(grep -R -i -n --include=\*.py "pdb.set_trace()" .) +if [ ! -z "$OUTPUT" ]; then + echo $OUTPUT + exit 1 +fi + diff --git a/search/__init__.py b/search/__init__.py new file mode 100644 index 0000000..196c92f --- /dev/null +++ b/search/__init__.py @@ -0,0 +1,9 @@ +from flask import Flask + +from search.search import get_search_blueprint + + +def create_app() -> Flask: + app = Flask(__name__) + app.register_blueprint(get_search_blueprint()) + return app diff --git a/search/search.py b/search/search.py new file mode 100644 index 0000000..6a74d0c --- /dev/null +++ b/search/search.py @@ -0,0 +1,18 @@ +from flask import Blueprint, Response + +from lxml import etree + +LIBERO_NAMESPACE = 'http://libero.pub' +NAMESPACE_MAP = {None: LIBERO_NAMESPACE} + + +def get_search_blueprint() -> Blueprint: + blueprint = Blueprint('search', __name__) + + @blueprint.route('/search', methods=['GET']) + def search() -> Response: # pylint: disable=unused-variable + root = etree.Element('item-list', nsmap=NAMESPACE_MAP) + response = etree.tostring(root, xml_declaration=True, encoding='UTF-8') + return Response(response=response, status=200, mimetype='application/xml') + + return blueprint diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..35361b3 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,28 @@ +[flake8] +max-line-length = 100 + +[isort] +default_section = THIRDPARTY +known_first_party = search +known_flask = flask +sections = FUTURE,STDLIB,FLASK,THIRDPARTY,FIRSTPARTY,LOCALFOLDER +not_skip = __init__.py + +[pylint] +disable = missing-docstring +extension-pkg-whitelist=lxml + +[mypy] +# must be present in file + +[mypy-flask] +# used to suppress MyPy warning for missing stubs +ignore_missing_imports = True + +[mypy-lxml] +# used to suppress MyPy warning for missing stubs +ignore_missing_imports = True + +[mypy-pytest] +# used to suppress MyPy warning for missing stubs +ignore_missing_imports = True diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..205bab3 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,11 @@ +from flask import Flask + +import pytest + +from search import create_app + + +@pytest.fixture(scope='module') +def client() -> Flask: + app = create_app() + return app.test_client() diff --git a/tests/test_search.py b/tests/test_search.py new file mode 100644 index 0000000..12bdfc8 --- /dev/null +++ b/tests/test_search.py @@ -0,0 +1,24 @@ +from io import BytesIO + +from lxml import etree + + +def test_empty_response(client) -> None: + response = client.get('/search') + xml = etree.parse(BytesIO(response.data)) + root = xml.getroot() + + assert root.getchildren() == [] + + +def test_empty_response_is_well_formed(client) -> None: + response = client.get('/search') + xml = etree.parse(BytesIO(response.data)) + root = xml.getroot() + + assert response.status_code == 200 + assert response.content_type == 'application/xml; charset=utf-8' + assert xml.docinfo.encoding == 'UTF-8' + assert xml.docinfo.xml_version == '1.0' + assert xml.docinfo.root_name == 'item-list' + assert root.tag == '{http://libero.pub}item-list' # check namespace