diff --git a/CHANGELOG.md b/CHANGELOG.md index a3d8648..fe9d2f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,18 +9,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - default reviewer added in `dependabot.yml` - `ReserverBaseError` added in `reserver/__init__.py` ### Changed +- `upload` method in `reserver_obj.py` - `README.md` updated - `AUTHORS.md` updated - GitHub actions are limited to the `dev` and `main` branches -- `generate_template_setup_py` method in `reserver_func.py` +- `generate_template_setup_py` function in `reserver_func.py` - `Python 3.13` added to `test.yml` +### Removed +- `does_package_exist` function in `reserver_func.py` ## [0.3] - 2024-08-28 ### Added - CLI tests added - `param` arg in CLI Handler - more testcases in conflict cases - `batch_upload` tests -- `read_json` method in `util.py` +- `read_json` function in `util.py` ### Changed - `README.md` updated - `upload` method in `reserver_obj.py` @@ -29,14 +32,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - `CLI` handler - `Python 3.6` support -- `has_named_parameter` method in `util.py` +- `has_named_parameter` function in `util.py` - `feature_request.yml` template - `config.yml` for issue template - `batch_upload` method in `PyPIUploader` - `SECURITY.md` ### Changed - `upload` method in `reserver_obj.py` -- `does_package_exist` method in `reserver_func.py` +- `does_package_exist` function in `reserver_func.py` - `test.yml` for `Python 3.6` support - Logo updated - Bug report template modified diff --git a/dev-requirements.txt b/dev-requirements.txt index 7d9d45c..3b0e837 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,5 +1,4 @@ art==6.4 -requests==2.32.3 setuptools==75.7.0 wheel==0.45.1 twine==6.0.1 diff --git a/requirements.txt b/requirements.txt index f5bf9df..1a0e53c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ art>=5.3 -requests>=1.0.0 setuptools>=40.8.0 wheel>=0.35.0 twine>=3.5.0 -chardet>=4.0.0 +chardet>=4.0.0 \ No newline at end of file diff --git a/reserver/reserver_func.py b/reserver/reserver_func.py index b824d18..7cabba8 100644 --- a/reserver/reserver_func.py +++ b/reserver/reserver_func.py @@ -1,14 +1,12 @@ # -*- coding: utf-8 -*- """Reserver functions.""" import re -import requests -import requests.adapters -from .reserver_param import PYPI_TEST_URL, PYPI_MAIN_URL, PACKAGE_PARAMETERS, VALIDATIONS, OVERVIEW -from .reserver_param import INVALID_PACKAGE_PARAMETER_NAME_ERROR, INVALID_PACKAGE_PARAMETER_VALUE_ERROR -from .reserver_errors import ReserverBaseError -from hashlib import sha256 from time import time +from hashlib import sha256 from os import mkdir, rmdir +from .reserver_param import PACKAGE_PARAMETERS, VALIDATIONS, OVERVIEW +from .reserver_param import INVALID_PACKAGE_PARAMETER_NAME_ERROR, INVALID_PACKAGE_PARAMETER_VALUE_ERROR +from .reserver_errors import ReserverBaseError def get_random_name(): @@ -20,37 +18,6 @@ def get_random_name(): return sha256(str(time()).encode("utf-8")).hexdigest() -def does_package_exist(suggested_name, test_pypi): - """ - Check whether a package with the given name exists or not. - - :param suggested_name: given name to search in pypi(or test.pypi) - :type suggested_name: str - :param test_pypi: indicates to search in test.pypi or not - :type test_pypi: bool - :return: whether given name does exist in the pypi or not(as a boolean value) - """ - if not isinstance(suggested_name, str): - suggested_name = str(suggested_name) - if test_pypi: - url = PYPI_TEST_URL + "/" + suggested_name + "/" - else: - url = PYPI_MAIN_URL + "/" + suggested_name + "/" - - s = requests.Session() - retries = requests.adapters.Retry( - total=5, - backoff_factor=0.1, - status_forcelist=[500, 502, 503, 504] - ) - - s.mount('http://', requests.adapters.HTTPAdapter(max_retries=retries)) - s.mount('https://', requests.adapters.HTTPAdapter(max_retries=retries)) - - response = s.get(url, timeout=5) - return not response.status_code == 404 - - def get_package_parameter(parameter, user_parameters, regex=None): """ Get the value for the associated package parameter. diff --git a/reserver/reserver_obj.py b/reserver/reserver_obj.py index 5c908d1..990d164 100644 --- a/reserver/reserver_obj.py +++ b/reserver/reserver_obj.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- """Reserver modules.""" import chardet +import platform from re import sub from sys import executable from os import environ, path, getcwd, remove from .reserver_errors import ReserverBaseError +from .reserver_func import generate_template_setup_py from subprocess import check_output, CalledProcessError from .reserver_param import UNEQUAL_PARAM_NAME_LENGTH_ERROR from .util import has_named_parameter, remove_dir, read_json -from .reserver_func import does_package_exist, generate_template_setup_py class PyPIUploader: @@ -71,15 +72,11 @@ def upload(self, package_name, user_parameters=None): :param package_name: package name :type package_name: str - :param user_parameters: path to the .json file containing user-defined package parameters - :type user_parameters: str + :param user_parameters: json file or path to the .json file containing user-defined package parameters + :type user_parameters: str | dict :return: True if the package is successfully reserved, False otherwise """ - if does_package_exist(package_name, self.test_pypi): - print("This package already exists in PyPI.") - return False - - if user_parameters is not None: + if not isinstance(user_parameters, dict) and user_parameters is not None: user_parameters = read_json(user_parameters) generate_template_setup_py(package_name, user_parameters) @@ -98,7 +95,10 @@ def upload(self, package_name, user_parameters=None): # prevent from uploading any other previously build library in this path. if path.exists(generated_dist_folder): remove_dir(generated_dist_folder) - commands = [f'"{executable}" "{generated_setup_file_path}" sdist bdist_wheel'] + if platform.system() == "Windows": + commands = [f'"{executable}" "{generated_setup_file_path}" sdist bdist_wheel > nul 2>&1'] + else: + commands = [f'"{executable}" "{generated_setup_file_path}" sdist bdist_wheel > /dev/null 2>&1'] if self.test_pypi: commands += [ f'"{executable}" -m twine upload --repository testpypi "{generated_tar_gz_file}"', @@ -106,8 +106,8 @@ def upload(self, package_name, user_parameters=None): ] else: commands += [ - f'"{executable}" -m twine upload --verbose "{generated_tar_gz_file}"', - f'"{executable}" -m twine upload --verbose "{generated_wheel_file}"', + f'"{executable}" -m twine upload "{generated_tar_gz_file}"', + f'"{executable}" -m twine upload "{generated_wheel_file}"', ] # Run the commands publish_failed = False @@ -125,22 +125,13 @@ def upload(self, package_name, user_parameters=None): error = error.decode(chardet.detect(error)['encoding']) except BaseException: error = error.decode('utf-8') - if command == commands[-2]: - if "403" in error and "Invalid or non-existent authentication information" in error: - error = "Invalid or non-existent authentication information(PyPI API Key)." - if "400" in error and "too similar to an existing project" in error: - error = "Given package name is too similar to an existing project in PyPI." - if "400" in error and "isn't allowed." in error: - error = "Given package name has conflict with the module name of a previously taken package." - if "400" in error and "isn't allowed (conflict with Python Standard Library" in error: - error = "Given package name has conflict with Python Standard Library module name." - break + # remove credential from env variables if "TWINE_USERNAME" in environ: environ.pop("TWINE_USERNAME") if "TWINE_PASSWORD" in environ: environ.pop("TWINE_PASSWORD") - + # remove previously generated files remove(generated_setup_file_path) remove_dir(generated_package_folder) remove_dir(generated_egginfo_file_path) diff --git a/reserver/reserver_param.py b/reserver/reserver_param.py index 92921e8..5c0389d 100644 --- a/reserver/reserver_param.py +++ b/reserver/reserver_param.py @@ -5,8 +5,6 @@ """ RESERVER_VERSION = "0.3" RESERVER_NAME = "reserver" -PYPI_TEST_URL = "https://test.pypi.org/project" -PYPI_MAIN_URL = "https://pypi.org/project" PACKAGE_PARAMETERS = { "description": "This name has been reserved using Reserver", "author": "Development Team", diff --git a/tests/test_reserver.py b/tests/test_reserver.py index b496773..7625094 100644 --- a/tests/test_reserver.py +++ b/tests/test_reserver.py @@ -1,9 +1,9 @@ +import os from reserver import PyPIUploader from reserver.reserver_func import get_random_name -import os -test_pypi_token = os.environ.get("TWINE_TEST_PASSWORD") pypi_token = os.environ.get("TWINE_PASSWORD") +test_pypi_token = os.environ.get("TWINE_TEST_PASSWORD") def test_package_exists(): # test reserved name @@ -15,31 +15,31 @@ def test_standard_module_conflict(): uploader = PyPIUploader(test_pypi_token, test_pypi=True) assert uploader.upload("os") == False -def test_batch_packages_names(): - # test batch of package names - uploader = PyPIUploader(test_pypi_token, test_pypi=True) - assert uploader.batch_upload(["numpy", "scikit-learn"]) == 0 - def test_valid_package_invalid_credentials(): # test not reserved name -> wrong credentials - wrong_pypi_token = "pypi-wrong-api-token" - uploader = PyPIUploader(wrong_pypi_token, test_pypi=True) + uploader = PyPIUploader("pypi-wrong-api-token", test_pypi=True) assert uploader.upload(get_random_name()) == False def test_valid_package_valid_credentials(): # test not reserved name -> correct credentials # uploader = PyPIUploader(test_pypi_token, test_pypi=True) - # uploader.upload(get_random_name()) + # assert uploader.upload(get_random_name()) == True assert True == True def test_module_conflict(): # try to reserve a name which conflicts with the module name of a previously taken package (the taken package itself has a different name, but it's module name has conflict)." - uploader = PyPIUploader(pypi_token, test_pypi=False) + uploader = PyPIUploader(test_pypi_token, test_pypi=True) assert uploader.upload("freeze") == False +def test_batch_packages_names(): + # test batch of package names + uploader = PyPIUploader(test_pypi_token, test_pypi=True) + assert uploader.batch_upload(["numpy", "scikit-learn"]) == 0 + def test_batch_upload(): # try to reserve two non taken package names with per package custom setup.py parameters - # uploader = PyPIUploader(test_pypi_token, True) + # make sure you are in "tests" directory + # uploader = PyPIUploader(test_pypi_token, test_pypi=True) # assert uploader.batch_upload( # [get_random_name(), get_random_name() + get_random_name()], # ["config.json", "config2.json"]