mirror of
https://github.com/bndr/pipreqs.git
synced 2025-06-06 03:25:21 +00:00
Merge e0cfb9abd3d2a3fb13b9b4611064489670b8ceee into 68f9b2859d45a60e699839f87b1b9558cd36a329
This commit is contained in:
commit
1e80996e36
6
.eggs/README.txt
Normal file
6
.eggs/README.txt
Normal file
@ -0,0 +1,6 @@
|
||||
This directory contains eggs that were downloaded by setuptools to build, test, and run plug-ins.
|
||||
|
||||
This directory caches those eggs to prevent repeated downloads.
|
||||
|
||||
However, it is safe to delete this directory.
|
||||
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,3 +1,11 @@
|
||||
# fix_SSLError -----
|
||||
.env38/
|
||||
.vscode/
|
||||
pipreqs_flake8_results.txt
|
||||
pipreqs_setup_test_results.txt
|
||||
pipreqs_tox_results.txt
|
||||
# ------------------
|
||||
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
|
29
.vscode/cspell.json
vendored
Normal file
29
.vscode/cspell.json
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "0.2",
|
||||
"ignorePaths": [],
|
||||
"dictionaryDefinitions": [],
|
||||
"dictionaries": [],
|
||||
"words": [
|
||||
"beautifulsoup",
|
||||
"boto",
|
||||
"compat",
|
||||
"docopt",
|
||||
"docstrings",
|
||||
"dotenv",
|
||||
"mkvirtualenv",
|
||||
"nonexistendmodule",
|
||||
"pipreqs",
|
||||
"pymongo",
|
||||
"pypi",
|
||||
"requiremnts",
|
||||
"savepath",
|
||||
"seasurf",
|
||||
"sqlalchemy",
|
||||
"ujson",
|
||||
"virtualenv",
|
||||
"virtualenvwrapper",
|
||||
"Yarg"
|
||||
],
|
||||
"ignoreWords": [],
|
||||
"import": []
|
||||
}
|
189
.vscode/extensions.json
vendored
Normal file
189
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
{
|
||||
"recommendations": [
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// VS Code Editor, Config, Spelling and Themes
|
||||
// -----------------------------------------------------------------------
|
||||
// - Code Spell Checker
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
// - EditorConfig for VS Code
|
||||
"editorconfig.editorconfig",
|
||||
// - Peacock
|
||||
"johnpapa.vscode-peacock",
|
||||
// - Night Owl
|
||||
"sdras.night-owl",
|
||||
// - indent-rainbow
|
||||
"oderwat.indent-rainbow",
|
||||
// - isort
|
||||
"ms-python.isort",
|
||||
// - Prettier - Code formatter
|
||||
"esbenp.prettier-vscode",
|
||||
// - vscode-icons
|
||||
"vscode-icons-team.vscode-icons",
|
||||
// - vscode-pdf
|
||||
"tomoki1207.pdf",
|
||||
// - Workspace Config+
|
||||
"swellaby.workspace-config-plus",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Python
|
||||
// -----------------------------------------------------------------------
|
||||
// - autoDocstring - Python Docstring Generator
|
||||
//"njpwerner.autodocstring",
|
||||
// - Django
|
||||
"batisteo.vscode-django",
|
||||
// - Pylance
|
||||
"ms-python.vscode-pylance",
|
||||
// - Python
|
||||
"ms-python.python",
|
||||
// - Python Environment Manager
|
||||
"donjayamanne.python-environment-manager",
|
||||
// - Python Extension Pack
|
||||
"donjayamanne.python-extension-pack",
|
||||
// - Python Indent
|
||||
"kevinrose.vsc-python-indent",
|
||||
// - Test Adapter Converter
|
||||
"ms-vscode.test-adapter-converter",
|
||||
// - Test Explorer UI
|
||||
"hbenl.vscode-test-explorer",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Rust
|
||||
// -----------------------------------------------------------------------
|
||||
// - Rust-Analyzer
|
||||
"rust-lang.rust-analyzer",
|
||||
// Better TOML
|
||||
"bungcip.better-toml",
|
||||
// - crates
|
||||
"serayuzgur.crates",
|
||||
// - Rust Test Explorer
|
||||
"swellaby.vscode-rust-test-adapter",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Docker
|
||||
// -----------------------------------------------------------------------
|
||||
// - Docker Compose
|
||||
"p1c2u.docker-compose",
|
||||
// - Dev Container
|
||||
"ms-vscode-remote.remote-containers",
|
||||
// - Docker
|
||||
"ms-azuretools.vscode-docker",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Data
|
||||
// -----------------------------------------------------------------------
|
||||
// - Excel Viewer
|
||||
"grapecity.gc-excelviewer",
|
||||
// - MongoDB for VS Code
|
||||
"mongodb.mongodb-vscode",
|
||||
// - PlantUML
|
||||
//"jebbs.plantuml",
|
||||
// - REST Client
|
||||
//"humao.rest-client",
|
||||
// - SQLite Viewer
|
||||
"qwtel.sqlite-viewer",
|
||||
// - Thunder Client
|
||||
"rangav.vscode-thunder-client",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Git
|
||||
// -----------------------------------------------------------------------
|
||||
// - Git Config User Profiles
|
||||
"onlyutkarsh.git-config-user-profiles",
|
||||
// - Git Graph
|
||||
"mhutchie.git-graph",
|
||||
// - Git History
|
||||
"donjayamanne.githistory",
|
||||
// - git-autoconfig
|
||||
"shyykoserhiy.git-autoconfig",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Jupyter Notebook
|
||||
// -----------------------------------------------------------------------
|
||||
// - Jupyter
|
||||
"ms-toolsai.jupyter",
|
||||
// - Jupyter Cell Tags
|
||||
"ms-toolsai.vscode-jupyter-cell-tags",
|
||||
// - Jupyter Keymap
|
||||
"ms-toolsai.jupyter-keymap",
|
||||
// - Jupyter Notebook Renderers
|
||||
"ms-toolsai.jupyter-renderers",
|
||||
// - Jupyter PowerToys
|
||||
//"ms-toolsai.vscode-jupyter-powertoys",
|
||||
// - Jupyter Slide Show
|
||||
"ms-toolsai.vscode-jupyter-slideshow",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Yaml and Json
|
||||
// -----------------------------------------------------------------------
|
||||
// - .NET Install Tool for Extension Authors
|
||||
"ms-dotnettools.vscode-dotnet-runtime",
|
||||
// - Azure Resource Manager (ARM) Tools
|
||||
"msazurermtools.azurerm-vscode-tools",
|
||||
// - YAML
|
||||
"redhat.vscode-yaml",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Markdown and reStructured Text
|
||||
// -----------------------------------------------------------------------
|
||||
// - Markdown All in One
|
||||
"yzhang.markdown-all-in-one",
|
||||
// - Markdown PDF
|
||||
"yzane.markdown-pdf",
|
||||
// - markdownlint
|
||||
"davidanson.vscode-markdownlint",
|
||||
// - Esbonio (conflicts with reStructuredText)
|
||||
//"swyddfa.esbonio",
|
||||
// - reStructuredText
|
||||
"lextudio.restructuredtext",
|
||||
// - reStructuredText Syntax highlighting
|
||||
"trond-snekvik.simple-rst",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// CSS
|
||||
// -----------------------------------------------------------------------
|
||||
// - Auto Rename Tag
|
||||
//"formulahendry.auto-rename-tag",
|
||||
// - IntelliSense for CSS class names in HTML
|
||||
//"zignd.html-css-class-completion",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// local web server
|
||||
// -----------------------------------------------------------------------
|
||||
// - Live Server
|
||||
//"ritwickdey.liveserver",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// JavaScript
|
||||
// -----------------------------------------------------------------------
|
||||
// - Babel JavaScript
|
||||
//"mgmcdermott.vscode-language-babel",
|
||||
// - ES7+ React/Redux/React-Native snippets
|
||||
//"dsznajder.es7-react-js-snippets",
|
||||
// - ESLint
|
||||
//"dbaeumer.vscode-eslint",
|
||||
// - JavaScript (ES6) code snippets
|
||||
//"xabikos.javascriptsnippets",
|
||||
// - JavaScript Debugger (Nightly)
|
||||
//"ms-vscode.js-debug-nightly",
|
||||
// - Material-UI Snippets
|
||||
//"vscodeshift.material-ui-snippets",
|
||||
// - TypeScript + Webpack Problem Matchers
|
||||
//"amodio.tsl-problem-matcher",
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Java
|
||||
// -----------------------------------------------------------------------
|
||||
// - Debugger for Java
|
||||
//"vscjava.vscode-java-debug",
|
||||
// - Extension Pack for Java
|
||||
//"vscjava.vscode-java-pack",
|
||||
// - Language Support for Java(TM) by Red Hat
|
||||
//"redhat.java",
|
||||
// - Maven for Java
|
||||
//"vscjava.vscode-maven",
|
||||
// - Project Manager for Java
|
||||
//"vscjava.vscode-java-dependency",
|
||||
// - Test Runner for Java
|
||||
//"vscjava.vscode-java-test",
|
||||
]
|
||||
}
|
104
.vscode/launch.json
vendored
Normal file
104
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// this will only work with a local python environment
|
||||
"name": "L-py-file",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true
|
||||
},
|
||||
{
|
||||
"name": "L-dj-server",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/src/manage.py",
|
||||
"args": [
|
||||
"runserver",
|
||||
],
|
||||
"django": true
|
||||
},
|
||||
{
|
||||
// launch local FastAPI on default port 8000
|
||||
// this will not work with a docker container
|
||||
"name": "L-p8000-api-uvicorn",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "uvicorn",
|
||||
"cwd": "${workspaceFolder}/src/api/",
|
||||
"args": [
|
||||
"startup:app",
|
||||
"--reload",
|
||||
"--port",
|
||||
"8000"
|
||||
],
|
||||
"jinja": true,
|
||||
"justMyCode": false
|
||||
},
|
||||
{
|
||||
//> Set PYDEVD_DISABLE_FILE_VALIDATION=1
|
||||
//> python -m debugpy --listen 8888 --wait-for-client startup.py
|
||||
// then select this script and start debug (F5)
|
||||
// finally, view FastAPI from the browser to hit breakpoints
|
||||
// http://0.0.0.0:8000
|
||||
// http://0.0.0.0:8000/docs
|
||||
// this is the default port 8000
|
||||
"name": "L-p8888-api-debugpy",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
"host": "localhost",
|
||||
"port": 8888
|
||||
},
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}/src/api/",
|
||||
"remoteRoot": "."
|
||||
}
|
||||
],
|
||||
"justMyCode": false
|
||||
},
|
||||
{
|
||||
"name": "C-p5678-app-debugpy",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
"host": "localhost",
|
||||
"port": 5678
|
||||
},
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}/src/app",
|
||||
"remoteRoot": "."
|
||||
}
|
||||
],
|
||||
"justMyCode": false
|
||||
},
|
||||
{
|
||||
//> Set PYDEVD_DISABLE_FILE_VALIDATION=1
|
||||
// run the docker container that has this entrypoint that forwards to our local port 9988
|
||||
// entrypoint: [ "python", "-m", "debugpy", "--listen", "0.0.0.0:$FASTAPI_DOCKER_DEBUG_PORT", "--wait-for-client", "startup.py" ]
|
||||
// then select this script and start debug (F5)
|
||||
// finally, view FastAPI from the browser to hit breakpoints
|
||||
// http://0.0.0.0:8011
|
||||
// http://0.0.0.0:8011/docs
|
||||
// FASTAPI_LOCAL_PORT used to forward to container port to the local port 8011
|
||||
"name": "C-p9988-api-debugpy",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
"host": "localhost",
|
||||
"port": 9988
|
||||
},
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}/src/api/",
|
||||
"remoteRoot": "."
|
||||
}
|
||||
],
|
||||
"justMyCode": false
|
||||
},
|
||||
]
|
||||
}
|
37
.vscode/settings.json
vendored
Normal file
37
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"python.pythonPath": "${workspaceFolder}/.env38/Scripts/python.exe",
|
||||
//
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#1e535f",
|
||||
"activityBar.background": "#1e535f",
|
||||
"activityBar.foreground": "#e7e7e7",
|
||||
"activityBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBarBadge.background": "#300f2a",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"commandCenter.border": "#e7e7e799",
|
||||
"sash.hoverBorder": "#1e535f",
|
||||
"statusBar.background": "#123138",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#1e535f",
|
||||
"statusBarItem.remoteBackground": "#123138",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#123138",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#12313899",
|
||||
"titleBar.inactiveForeground": "#e7e7e799"
|
||||
},
|
||||
//JWL4-BlueGreen
|
||||
"peacock.color": "#123138",
|
||||
//
|
||||
// reStructuredText, root ReadMe.rst
|
||||
//"esbonio.sphinx.confDir": "",
|
||||
//
|
||||
// test
|
||||
"python.testing.pytestArgs": [
|
||||
"src",
|
||||
"tests",
|
||||
],
|
||||
"python.testing.unittestEnabled": true,
|
||||
"python.testing.pytestEnabled": false,
|
||||
"esbonio.sphinx.confDir": "",
|
||||
}
|
@ -66,7 +66,7 @@ Ready to contribute? Here's how to set up `pipreqs` for local development.
|
||||
|
||||
$ mkvirtualenv pipreqs
|
||||
$ cd pipreqs/
|
||||
$ python setup.py develop
|
||||
$ python setup.py develop (or $pip install -e .)
|
||||
|
||||
4. Create a branch for local development::
|
||||
|
||||
@ -80,7 +80,13 @@ Ready to contribute? Here's how to set up `pipreqs` for local development.
|
||||
$ python setup.py test
|
||||
$ tox
|
||||
|
||||
To get flake8 and tox, just pip install them into your virtualenv.
|
||||
To get flake8 and tox, just pip install them into your virtualenv. (or $pip install -r requirements-dev.txt)
|
||||
|
||||
You may also need to provide `CA_BUNDLE` as an environment variable or parameter in the `tests/.env.test` file.
|
||||
|
||||
$ export CA_BUNDLE="/certs/path/certificates.pem" # for nix OS
|
||||
|
||||
$ set CA_BUNDLE="C:/certs/path/certificates.pem" # for win OS
|
||||
|
||||
6. Commit your changes and push your branch to GitHub::
|
||||
|
||||
@ -99,9 +105,9 @@ Before you submit a pull request, check that it meets these guidelines:
|
||||
2. If the pull request adds functionality, the docs should be updated. Put
|
||||
your new functionality into a function with a docstring, and add the
|
||||
feature to the list in README.rst.
|
||||
3. The pull request should work for Python 3.7 to 3.11, and PyPy. Check
|
||||
https://travis-ci.org/bndr/pipreqs/pull_requests and make sure that the
|
||||
tests pass for all supported Python versions.
|
||||
3. The pull request should work for Python 3.7 to 3.10 (3.11 needs work),
|
||||
and PyPy3. Check https://travis-ci.org/bndr/pipreqs/pull_requests and
|
||||
make sure that the tests pass for all supported Python versions.
|
||||
|
||||
Tips
|
||||
----
|
||||
|
@ -44,6 +44,12 @@ Usage
|
||||
environments parameter in your terminal:
|
||||
$ export HTTP_PROXY="http://10.10.1.10:3128"
|
||||
$ export HTTPS_PROXY="https://10.10.1.10:1080"
|
||||
--verify <ca_bundle> Use verify to provide a CA_BUNDLE file or directory
|
||||
with certificates of trusted CAs
|
||||
You can also just set the environment variable in
|
||||
your terminal: (`export` for nix and `set` for win)
|
||||
$ export CA_BUNDLE="/certs/path/certificates.pem"
|
||||
$ set CA_BUNDLE="C:/certs/path/certificates.pem"
|
||||
--debug Print debug information
|
||||
--ignore <dirs>... Ignore extra directories, each separated by a comma
|
||||
--no-follow-links Do not follow symbolic links in the project
|
||||
|
@ -18,6 +18,12 @@ Options:
|
||||
parameter in your terminal:
|
||||
$ export HTTP_PROXY="http://10.10.1.10:3128"
|
||||
$ export HTTPS_PROXY="https://10.10.1.10:1080"
|
||||
--verify <ca_bundle> Use verify to provide a CA_BUNDLE file or directory
|
||||
with certificates of trusted CAs
|
||||
You can also just set the environment variable in
|
||||
your terminal: (`export` for nix and `set` for win)
|
||||
$ export CA_BUNDLE="/certs/path/certificates.pem" #or
|
||||
$ set CA_BUNDLE="C:/certs/path/certificates.pem"
|
||||
--debug Print debug information
|
||||
--ignore <dirs>... Ignore extra directories, each separated by a comma
|
||||
--no-follow-links Do not follow symbolic links in the project
|
||||
@ -36,15 +42,16 @@ Options:
|
||||
<gt> | e.g. Flask>=1.1.2
|
||||
<no-pin> | e.g. Flask
|
||||
"""
|
||||
from contextlib import contextmanager
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import logging
|
||||
import ast
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
from docopt import docopt
|
||||
from contextlib import contextmanager
|
||||
|
||||
import requests
|
||||
from docopt import docopt
|
||||
from yarg import json2package
|
||||
from yarg.exceptions import HTTPError
|
||||
|
||||
@ -55,6 +62,7 @@ REGEXP = [
|
||||
re.compile(r'^from ((?!\.+).*?) import (?:.*)$')
|
||||
]
|
||||
|
||||
CA_BUNDLE = os.environ.get("CA_BUNDLE")
|
||||
|
||||
@contextmanager
|
||||
def _open(filename=None, mode='r'):
|
||||
@ -171,7 +179,12 @@ def output_requirements(imports, symbol):
|
||||
|
||||
|
||||
def get_imports_info(
|
||||
imports, pypi_server="https://pypi.python.org/pypi/", proxy=None):
|
||||
imports,
|
||||
pypi_server="https://pypi.python.org/pypi/",
|
||||
proxy=None,
|
||||
verify=CA_BUNDLE,
|
||||
c=None,
|
||||
):
|
||||
result = []
|
||||
|
||||
for item in imports:
|
||||
@ -182,7 +195,10 @@ def get_imports_info(
|
||||
item
|
||||
)
|
||||
response = requests.get(
|
||||
"{0}{1}/json".format(pypi_server, item), proxies=proxy)
|
||||
"{0}{1}/json".format(pypi_server, item),
|
||||
proxies=proxy,
|
||||
verify=verify,
|
||||
)
|
||||
if response.status_code == 200:
|
||||
if hasattr(response.content, 'decode'):
|
||||
data = json2package(response.content.decode())
|
||||
@ -459,6 +475,7 @@ def init(args):
|
||||
candidates = get_pkg_names(candidates)
|
||||
logging.debug("Found imports: " + ", ".join(candidates))
|
||||
pypi_server = "https://pypi.python.org/pypi/"
|
||||
verify = None
|
||||
proxy = None
|
||||
if args["--pypi-server"]:
|
||||
pypi_server = args["--pypi-server"]
|
||||
@ -466,6 +483,9 @@ def init(args):
|
||||
if args["--proxy"]:
|
||||
proxy = {'http': args["--proxy"], 'https': args["--proxy"]}
|
||||
|
||||
if args["--verify"]:
|
||||
verify = args["--verify"]
|
||||
|
||||
if args["--use-local"]:
|
||||
logging.debug(
|
||||
"Getting package information ONLY from local installation.")
|
||||
@ -489,6 +509,7 @@ def init(args):
|
||||
|
||||
imports = local + get_imports_info(difference,
|
||||
proxy=proxy,
|
||||
verify=verify,
|
||||
pypi_server=pypi_server)
|
||||
# sort imports based on lowercase name of package, similar to `pip freeze`.
|
||||
imports = sorted(imports, key=lambda x: x['name'].lower())
|
||||
|
36
requirements-dev.txt
Normal file
36
requirements-dev.txt
Normal file
@ -0,0 +1,36 @@
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# > python38 -m venv .env38
|
||||
# # activate (.env38) virtual environment
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# upgrade pip
|
||||
# (.env38)> python.exe -m pip install --upgrade pip
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# development packages
|
||||
# (.env38)> pip install -r requirements-dev.txt
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# development mode installation of `pipreqs`
|
||||
# (.env38)> pip install -e .
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# fix_SSLError tests.settings (optional) use of .env file
|
||||
# alternative would be to set CA_BUNDLE environment variable
|
||||
# $ export CA_BUNDLE="/certs/path/certificates.pem" # for nix OS
|
||||
# $ set CA_BUNDLE="C:/certs/path/certificates.pem" # for win OS
|
||||
|
||||
python-dotenv
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# required per CONTRIBUTING workflow
|
||||
flake8
|
||||
tox # only needed in the environment from which tox is run
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# (optional)
|
||||
# vscode settings: "python.formatting.provider": "black",
|
||||
#black
|
||||
docutils # reStructured Text support
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
@ -1,3 +1,4 @@
|
||||
wheel==0.38.1
|
||||
Yarg==0.1.9
|
||||
docopt==0.6.2
|
||||
docopt==0.6.2
|
||||
requests==2.28.2
|
||||
|
7
setup.py
7
setup.py
@ -7,7 +7,6 @@ except ImportError:
|
||||
|
||||
from pipreqs import __version__
|
||||
|
||||
|
||||
with open('README.rst') as readme_file:
|
||||
readme = readme_file.read()
|
||||
|
||||
@ -15,7 +14,10 @@ with open('HISTORY.rst') as history_file:
|
||||
history = history_file.read().replace('.. :changelog:', '')
|
||||
|
||||
requirements = [
|
||||
'docopt', 'yarg'
|
||||
'docopt', 'yarg', 'requests'
|
||||
]
|
||||
tests_requirements = [
|
||||
'python-dotenv', 'flake8'
|
||||
]
|
||||
|
||||
setup(
|
||||
@ -49,6 +51,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
],
|
||||
tests_require=tests_requirements,
|
||||
test_suite='tests',
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
|
8
tests/.env.test.example
Normal file
8
tests/.env.test.example
Normal file
@ -0,0 +1,8 @@
|
||||
# create a new file named `.env.test`
|
||||
# and assign CA_BUNDLE to your system path\ca.pem file
|
||||
|
||||
CA_BUNDLE=C:\your\path\and\certificates.pem
|
||||
|
||||
# alternatively you can set this value as an environment variable
|
||||
# $ set CA_BUNDLE="C:\your\path\and\certificates.pem" # for win OS
|
||||
# $ export CA_BUNDLE="C:\your\path\and\certificates.pem" # for nix OS
|
3
tests/.gitignore
vendored
Normal file
3
tests/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# .env.test is system specific
|
||||
# do not check in to repository
|
||||
.env.test
|
12
tests/_data_ignore/requirements.txt
Normal file
12
tests/_data_ignore/requirements.txt
Normal file
@ -0,0 +1,12 @@
|
||||
asposestorage==1.0.2
|
||||
beautifulsoup4==4.11.1
|
||||
boto==2.49.0
|
||||
docopt==0.6.2
|
||||
Flask==2.2.2
|
||||
ipython==8.8.0
|
||||
nose==1.3.7
|
||||
peewee==3.15.4
|
||||
pyflakes==3.0.1
|
||||
requests==2.28.2
|
||||
SQLAlchemy==1.4.46
|
||||
ujson==5.7.0
|
28
tests/settings.py
Normal file
28
tests/settings.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""
|
||||
Environment variables can be used as a first choice
|
||||
$ set CA_BUNDLE="certificates.pem" # for win OS
|
||||
$ export CA_BUNDLE="certificates.pem" # for nix OS
|
||||
|
||||
If environment variables are not found then a second attempt
|
||||
will be made by loading the values from a .env.test file in
|
||||
the same directory
|
||||
|
||||
See ./env.test.example for details.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
import os
|
||||
|
||||
CA_BUNDLE = os.environ.get("CA_BUNDLE")
|
||||
|
||||
if CA_BUNDLE is None and importlib.find_loader("dotenv"):
|
||||
# optional loading of values from .env.test file
|
||||
from pathlib import Path
|
||||
|
||||
import dotenv
|
||||
|
||||
env_test_path = Path(os.path.dirname(__file__) + "/.env.test")
|
||||
config = dotenv.dotenv_values(env_test_path)
|
||||
|
||||
if config is not None:
|
||||
CA_BUNDLE = config["CA_BUNDLE"]
|
@ -6,14 +6,25 @@ test_pipreqs
|
||||
----------------------------------
|
||||
|
||||
Tests for `pipreqs` module.
|
||||
|
||||
Environment variables used to mock arguments
|
||||
e.g.,
|
||||
$ set CA_BUNDLE="certificates.pem" # for win OS
|
||||
$ export CA_BUNDLE="certificates.pem" # for nix OS
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import requests
|
||||
|
||||
from pipreqs import pipreqs
|
||||
|
||||
CA_BUNDLE = os.environ.get("CA_BUNDLE")
|
||||
|
||||
if CA_BUNDLE is None:
|
||||
from tests.settings import CA_BUNDLE
|
||||
|
||||
|
||||
class TestPipreqs(unittest.TestCase):
|
||||
|
||||
@ -49,6 +60,7 @@ class TestPipreqs(unittest.TestCase):
|
||||
"requirements2.txt"
|
||||
)
|
||||
|
||||
|
||||
def test_get_all_imports(self):
|
||||
imports = pipreqs.get_all_imports(self.project)
|
||||
self.assertEqual(len(imports), 15)
|
||||
@ -80,7 +92,9 @@ class TestPipreqs(unittest.TestCase):
|
||||
Test to see that the right number of packages were found on PyPI
|
||||
"""
|
||||
imports = pipreqs.get_all_imports(self.project)
|
||||
with_info = pipreqs.get_imports_info(imports)
|
||||
with_info = pipreqs.get_imports_info(
|
||||
imports, proxy=None, verify=CA_BUNDLE
|
||||
)
|
||||
# Should contain 10 items without the "nonexistendmodule" and
|
||||
# "after_method_is_valid_even_if_not_pep8"
|
||||
self.assertEqual(len(with_info), 13)
|
||||
@ -113,9 +127,21 @@ class TestPipreqs(unittest.TestCase):
|
||||
"""
|
||||
Test that all modules we will test upon are in requirements file
|
||||
"""
|
||||
pipreqs.init({'<path>': self.project, '--savepath': None, '--print': False,
|
||||
'--use-local': None, '--force': True, '--proxy':None, '--pypi-server':None,
|
||||
'--diff': None, '--clean': None, '--mode': None})
|
||||
pipreqs.init(
|
||||
{
|
||||
"<path>": self.project,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": None,
|
||||
"--force": True,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--diff": None,
|
||||
"--clean": None,
|
||||
"--mode": None,
|
||||
}
|
||||
)
|
||||
assert os.path.exists(self.requirements_path) == 1
|
||||
with open(self.requirements_path, "r") as f:
|
||||
data = f.read().lower()
|
||||
@ -130,9 +156,21 @@ class TestPipreqs(unittest.TestCase):
|
||||
Test that items listed in requirements.text are the same
|
||||
as locals expected
|
||||
"""
|
||||
pipreqs.init({'<path>': self.project, '--savepath': None, '--print': False,
|
||||
'--use-local': True, '--force': True, '--proxy':None, '--pypi-server':None,
|
||||
'--diff': None, '--clean': None, '--mode': None})
|
||||
pipreqs.init(
|
||||
{
|
||||
"<path>": self.project,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": True,
|
||||
"--force": True,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--diff": None,
|
||||
"--clean": None,
|
||||
"--mode": None,
|
||||
}
|
||||
)
|
||||
assert os.path.exists(self.requirements_path) == 1
|
||||
with open(self.requirements_path, "r") as f:
|
||||
data = f.readlines()
|
||||
@ -145,9 +183,20 @@ class TestPipreqs(unittest.TestCase):
|
||||
Test that we can save requirements.txt correctly
|
||||
to a different path
|
||||
"""
|
||||
pipreqs.init({'<path>': self.project, '--savepath': self.alt_requirement_path,
|
||||
'--use-local': None, '--proxy':None, '--pypi-server':None, '--print': False,
|
||||
'--diff': None, '--clean': None, '--mode': None})
|
||||
pipreqs.init(
|
||||
{
|
||||
"<path>": self.project,
|
||||
"--savepath": self.alt_requirement_path,
|
||||
"--use-local": None,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--print": False,
|
||||
"--diff": None,
|
||||
"--clean": None,
|
||||
"--mode": None,
|
||||
}
|
||||
)
|
||||
assert os.path.exists(self.alt_requirement_path) == 1
|
||||
with open(self.alt_requirement_path, "r") as f:
|
||||
data = f.read().lower()
|
||||
@ -163,9 +212,21 @@ class TestPipreqs(unittest.TestCase):
|
||||
"""
|
||||
with open(self.requirements_path, "w") as f:
|
||||
f.write("should_not_be_overwritten")
|
||||
pipreqs.init({'<path>': self.project, '--savepath': None, '--use-local': None,
|
||||
'--force': None, '--proxy':None, '--pypi-server':None, '--print': False,
|
||||
'--diff': None, '--clean': None, '--mode': None})
|
||||
pipreqs.init(
|
||||
{
|
||||
"<path>": self.project,
|
||||
"--savepath": None,
|
||||
"--use-local": None,
|
||||
"--force": None,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--print": False,
|
||||
"--diff": None,
|
||||
"--clean": None,
|
||||
"--mode": None,
|
||||
}
|
||||
)
|
||||
assert os.path.exists(self.requirements_path) == 1
|
||||
with open(self.requirements_path, "r") as f:
|
||||
data = f.read().lower()
|
||||
@ -192,25 +253,39 @@ class TestPipreqs(unittest.TestCase):
|
||||
Test that trying to get a custom pypi sever fails correctly
|
||||
"""
|
||||
self.assertRaises(
|
||||
requests.exceptions.MissingSchema, pipreqs.init,
|
||||
{'<path>': self.project, '--savepath': None, '--print': False,
|
||||
'--use-local': None, '--force': True, '--proxy': None,
|
||||
'--pypi-server': 'nonexistent'}
|
||||
)
|
||||
requests.exceptions.MissingSchema,
|
||||
pipreqs.init,
|
||||
{
|
||||
"<path>": self.project,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": None,
|
||||
"--force": True,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": "nonexistent",
|
||||
},
|
||||
)
|
||||
|
||||
def test_ignored_directory(self):
|
||||
"""
|
||||
Test --ignore parameter
|
||||
"""
|
||||
pipreqs.init(
|
||||
{'<path>': self.project_with_ignore_directory, '--savepath': None,
|
||||
'--print': False, '--use-local': None, '--force': True,
|
||||
'--proxy':None, '--pypi-server':None,
|
||||
'--ignore':'.ignored_dir,.ignore_second',
|
||||
'--diff': None,
|
||||
'--clean': None,
|
||||
'--mode': None
|
||||
}
|
||||
{
|
||||
"<path>": self.project_with_ignore_directory,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": None,
|
||||
"--force": True,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--ignore": ".ignored_dir,.ignore_second",
|
||||
"--diff": None,
|
||||
"--clean": None,
|
||||
"--mode": None,
|
||||
}
|
||||
)
|
||||
with open(os.path.join(self.project_with_ignore_directory, "requirements.txt"), "r") as f:
|
||||
data = f.read().lower()
|
||||
@ -222,13 +297,19 @@ class TestPipreqs(unittest.TestCase):
|
||||
Test --mode=no-pin
|
||||
"""
|
||||
pipreqs.init(
|
||||
{'<path>': self.project_with_ignore_directory, '--savepath': None,
|
||||
'--print': False, '--use-local': None, '--force': True,
|
||||
'--proxy': None, '--pypi-server': None,
|
||||
'--diff': None,
|
||||
'--clean': None,
|
||||
'--mode': 'no-pin'
|
||||
}
|
||||
{
|
||||
"<path>": self.project_with_ignore_directory,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": None,
|
||||
"--force": True,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--diff": None,
|
||||
"--clean": None,
|
||||
"--mode": "no-pin",
|
||||
}
|
||||
)
|
||||
with open(os.path.join(self.project_with_ignore_directory, "requirements.txt"), "r") as f:
|
||||
data = f.read().lower()
|
||||
@ -240,14 +321,19 @@ class TestPipreqs(unittest.TestCase):
|
||||
Test --mode=gt
|
||||
"""
|
||||
pipreqs.init(
|
||||
{'<path>': self.project_with_ignore_directory, '--savepath': None, '--print': False,
|
||||
'--use-local': None, '--force': True,
|
||||
'--proxy': None,
|
||||
'--pypi-server': None,
|
||||
'--diff': None,
|
||||
'--clean': None,
|
||||
'--mode': 'gt'
|
||||
}
|
||||
{
|
||||
"<path>": self.project_with_ignore_directory,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": None,
|
||||
"--force": True,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--diff": None,
|
||||
"--clean": None,
|
||||
"--mode": "gt",
|
||||
}
|
||||
)
|
||||
with open(os.path.join(self.project_with_ignore_directory, "requirements.txt"), "r") as f:
|
||||
data = f.readlines()
|
||||
@ -261,14 +347,19 @@ class TestPipreqs(unittest.TestCase):
|
||||
Test --mode=compat
|
||||
"""
|
||||
pipreqs.init(
|
||||
{'<path>': self.project_with_ignore_directory, '--savepath': None, '--print': False,
|
||||
'--use-local': None, '--force': True,
|
||||
'--proxy': None,
|
||||
'--pypi-server': None,
|
||||
'--diff': None,
|
||||
'--clean': None,
|
||||
'--mode': 'compat'
|
||||
}
|
||||
{
|
||||
"<path>": self.project_with_ignore_directory,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": None,
|
||||
"--force": True,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--diff": None,
|
||||
"--clean": None,
|
||||
"--mode": "compat",
|
||||
}
|
||||
)
|
||||
with open(os.path.join(self.project_with_ignore_directory, "requirements.txt"), "r") as f:
|
||||
data = f.readlines()
|
||||
@ -282,18 +373,36 @@ class TestPipreqs(unittest.TestCase):
|
||||
Test --clean parameter
|
||||
"""
|
||||
pipreqs.init(
|
||||
{'<path>': self.project, '--savepath': None, '--print': False,
|
||||
'--use-local': None, '--force': True, '--proxy': None,
|
||||
'--pypi-server': None, '--diff': None, '--clean': None,
|
||||
'--mode': None}
|
||||
)
|
||||
{
|
||||
"<path>": self.project,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": None,
|
||||
"--force": True,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--diff": None,
|
||||
"--clean": None,
|
||||
"--mode": None,
|
||||
}
|
||||
)
|
||||
assert os.path.exists(self.requirements_path) == 1
|
||||
pipreqs.init(
|
||||
{'<path>': self.project, '--savepath': None, '--print': False,
|
||||
'--use-local': None, '--force': None, '--proxy': None,
|
||||
'--pypi-server': None, '--diff': None,
|
||||
'--clean': self.requirements_path, '--mode': 'non-pin'}
|
||||
)
|
||||
{
|
||||
"<path>": self.project,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": None,
|
||||
"--force": None,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--diff": None,
|
||||
"--clean": self.requirements_path,
|
||||
"--mode": "non-pin",
|
||||
}
|
||||
)
|
||||
with open(self.requirements_path, "r") as f:
|
||||
data = f.read().lower()
|
||||
for item in self.modules[:-3]:
|
||||
@ -305,19 +414,37 @@ class TestPipreqs(unittest.TestCase):
|
||||
"""
|
||||
cleaned_module = 'sqlalchemy'
|
||||
pipreqs.init(
|
||||
{'<path>': self.project, '--savepath': None, '--print': False,
|
||||
'--use-local': None, '--force': True, '--proxy': None,
|
||||
'--pypi-server': None, '--diff': None, '--clean': None,
|
||||
'--mode': None}
|
||||
)
|
||||
{
|
||||
"<path>": self.project,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": None,
|
||||
"--force": True,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--diff": None,
|
||||
"--clean": None,
|
||||
"--mode": None,
|
||||
}
|
||||
)
|
||||
assert os.path.exists(self.requirements_path) == 1
|
||||
modules_clean = [m for m in self.modules if m != cleaned_module]
|
||||
pipreqs.init(
|
||||
{'<path>': self.project_clean, '--savepath': None,
|
||||
'--print': False, '--use-local': None, '--force': None,
|
||||
'--proxy': None, '--pypi-server': None, '--diff': None,
|
||||
'--clean': self.requirements_path, '--mode': 'non-pin'}
|
||||
)
|
||||
{
|
||||
"<path>": self.project_clean,
|
||||
"--savepath": None,
|
||||
"--print": False,
|
||||
"--use-local": None,
|
||||
"--force": None,
|
||||
"--proxy": None,
|
||||
"--verify": CA_BUNDLE,
|
||||
"--pypi-server": None,
|
||||
"--diff": None,
|
||||
"--clean": self.requirements_path,
|
||||
"--mode": "non-pin",
|
||||
}
|
||||
)
|
||||
with open(self.requirements_path, "r") as f:
|
||||
data = f.read().lower()
|
||||
self.assertTrue(cleaned_module not in data)
|
||||
|
40
tox.ini
40
tox.ini
@ -1,11 +1,12 @@
|
||||
[tox]
|
||||
envlist = py37, py38, py39, pypy3, flake8
|
||||
envlist = py37, py38, py39, py310, pypy3, flake8
|
||||
|
||||
[gh-actions]
|
||||
python =
|
||||
3.7: py37
|
||||
3.8: py38
|
||||
3.9: py39
|
||||
3.10: py310
|
||||
pypy-3.7: pypy3
|
||||
|
||||
[testenv]
|
||||
@ -13,4 +14,39 @@ setenv =
|
||||
PYTHONPATH = {toxinidir}:{toxinidir}/pipreqs
|
||||
commands = python setup.py test
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/requirements.txt
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# tox -e dev311
|
||||
# Python 3.11 runtime error
|
||||
# - py310 and py311 were not tested prior to this submission
|
||||
# - not related to this submission (but this needs further debug to understand)
|
||||
#
|
||||
# ======================================================================
|
||||
# FAIL: test_get_use_local_only (tests.test_pipreqs.TestPipreqs.test_get_use_local_only)
|
||||
# Test without checking PyPI, check to see if names of local
|
||||
# ----------------------------------------------------------------------
|
||||
# Traceback (most recent call last):
|
||||
# File "c:\code\ghcwm\py_dev\src\autoreq\pub\c-w-m\pipreqs\tests\test_pipreqs.py", line 127, in test_get_use_local_only
|
||||
# self.assertTrue(item["name"].lower() in self.local)
|
||||
# AssertionError: False is not true
|
||||
#
|
||||
# ======================================================================
|
||||
# FAIL: test_init_local_only (tests.test_pipreqs.TestPipreqs.test_init_local_only)
|
||||
# Test that items listed in requirements.text are the same
|
||||
# ----------------------------------------------------------------------
|
||||
# Traceback (most recent call last):
|
||||
# File "c:\code\ghcwm\py_dev\src\autoreq\pub\c-w-m\pipreqs\tests\test_pipreqs.py", line 182, in test_init_local_only
|
||||
# self.assertTrue(item[0].lower() in self.local)
|
||||
# AssertionError: False is not true
|
||||
#
|
||||
# ----------------------------------------------------------------------
|
||||
[testenv:dev311]
|
||||
description = development mode environment for debug of py311
|
||||
basepython = python3.11
|
||||
deps = {[testenv]deps}
|
||||
# development mode for debuging
|
||||
usedevelop = True
|
||||
commands = python setup.py test
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
Loading…
x
Reference in New Issue
Block a user