diff --git a/.github/workflows/check-master.yml b/.github/workflows/check-master.yml index 59908914..3203a694 100644 --- a/.github/workflows/check-master.yml +++ b/.github/workflows/check-master.yml @@ -51,7 +51,7 @@ jobs: conda info conda config --show conda list --show-channel-urls - echo ::set-output name=exit_status::success + echo "exit_status=success" >> "$GITHUB_OUTPUT" - name: Check with pycodestyle if: ${{ always() && steps.conda_environment_information.outputs.exit_status == 'success' }} @@ -93,7 +93,7 @@ jobs: fail-fast: false matrix: os: [ macos-latest, ubuntu-latest, windows-latest ] - python-version: [ '3.8', '3.9', '3.10', '3.11' ] + python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12' ] steps: # most of these steps might be anchored to corresponding ones in the "lint_code" job diff --git a/CHANGELOG.md b/CHANGELOG.md index d551066e..46cdf1f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,23 @@ We [keep a changelog.](http://keepachangelog.com/) -## 1.12.2 +## 1.12.3 - 2024-02-22 + +### Tickets closed + +* AC-207 - Accessing entry_points by index no longer supported in Python 3.12 +* AC-208 - Fix uploads of existing packages + +### Pull requests merged + +* [PR 708](https://github.com/Anaconda-Platform/anaconda-client/pull/708) - AC-208: fix uploads of existing packages +* [PR 705](https://github.com/Anaconda-Platform/anaconda-client/pull/705) - AC-207: add importlib.metadata python3.12 compatibility +* [PR 701](https://github.com/Anaconda-Platform/anaconda-client/pull/701) - Restore default user config search path on Linux +* [PR 700](https://github.com/Anaconda-Platform/anaconda-client/pull/700) - ci: Use GITHUB_OUTPUT envvar instead of set-output command +* [PR 698](https://github.com/Anaconda-Platform/anaconda-client/pull/698) - AC-197: python 3.12 support +* [PR 696](https://github.com/Anaconda-Platform/anaconda-client/pull/696) - Make arch and platform inside index.json optional + +## 1.12.2 - 2023-11-16 ### Tickets closed diff --git a/Makefile b/Makefile index f85cdbc7..f9bc029d 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ help: init: @if [ -z "$${CONDA_SHLVL:+x}" ]; then echo "Conda is not installed." && exit 1; fi - @conda create -y -n anaconda_client python=3.8 --file requirements.txt --file requirements-extra.txt + @conda create -y -n anaconda_client python=3.12 --file requirements.txt --file requirements-extra.txt @conda run -n anaconda_client pip install -r requirements-dev.txt @echo "\n\nConda environment has been created. To activate run \"conda activate anaconda_client\"." diff --git a/binstar_client/__about__.py b/binstar_client/__about__.py index 7c187431..301d5b71 100644 --- a/binstar_client/__about__.py +++ b/binstar_client/__about__.py @@ -4,4 +4,4 @@ __all__ = ['__version__'] -__version__ = '1.12.2' +__version__ = '1.12.3' diff --git a/binstar_client/commands/upload.py b/binstar_client/commands/upload.py index f484cdb0..bccab625 100644 --- a/binstar_client/commands/upload.py +++ b/binstar_client/commands/upload.py @@ -522,6 +522,11 @@ def upload_package(self, filename: str, package_meta: detect.Meta) -> bool: meta.release_attrs['summary'] = self.arguments.summary if self.arguments.description is not None: meta.release_attrs['description'] = self.arguments.description + if (meta.package_type is PackageType.CONDA) and (not self.arguments.keep_basename): + meta.rebuild_basename() + + if not self._check_file(meta): + return False logger.info('Creating package "%s"', meta.name) package: PackageCacheRecord = self.get_package(meta) @@ -531,9 +536,6 @@ def upload_package(self, filename: str, package_meta: detect.Meta) -> bool: logger.info('Creating release "%s"', meta.version) self.get_release(meta) - if (meta.package_type is PackageType.CONDA) and (not self.arguments.keep_basename): - meta.rebuild_basename() - logger.info('Uploading file "%s/%s/%s/%s"', self.username, meta.name, meta.version, meta.file_attrs['basename']) return self._upload_file(meta) @@ -543,72 +545,71 @@ def upload_project(self, filename: str) -> bool: self.uploaded_projects.append(uploaded_project) return True - def _upload_file(self, meta: PackageMeta) -> bool: - """Perform upload of a file after its metadata and related package and release are prepared.""" + def _check_file(self, meta: PackageMeta) -> bool: + """""" basename: str = meta.file_attrs['basename'] - package_type: typing.Union[PackageType, str] = meta.file_attrs.pop('binstar_package_type', meta.package_type) + try: + self.api.distribution(self.username, meta.name, meta.version, basename) + except errors.NotFound: + return True - step: int - for step in range(2): - try: - stream: typing.BinaryIO - with open(meta.filename, 'rb') as stream: - result: typing.Mapping[str, typing.Any] = self.api.upload( - self.username, - meta.name, - meta.version, - basename, - stream, - package_type, - self.arguments.description, - dependencies=meta.file_attrs.get('dependencies'), - attrs=meta.file_attrs['attrs'], - channels=self.arguments.labels, - ) + if self.arguments.mode == 'skip': + logger.info('Distribution already exists. Skipping upload.\n') + return False - self.uploaded_packages.append({ - 'package_type': meta.package_type, - 'username': self.username, - 'name': meta.name, - 'version': meta.version, - 'basename': basename, - 'url': result.get('url', f'https://anaconda.org/{self.username}/{meta.name}'), - }) - self.__package_cache[meta.package_key].update(meta.package_type) - self.__release_cache[meta.release_key].update() - logger.info('Upload complete\n') + if self.arguments.mode == 'force': + logger.warning('Distribution "%s" already exists. Removing.', basename) + self.api.remove_dist(self.username, meta.name, meta.version, basename) + return True + + if self.arguments.mode == 'interactive': + if bool_input(f'Distribution "{basename}" already exists. Would you like to replace it?'): + self.api.remove_dist(self.username, meta.name, meta.version, basename) return True + logger.info('Not replacing distribution "%s"', basename) + return False - except errors.Conflict: - if step: - raise - - if self.arguments.mode == 'skip': - logger.info('Distribution already exists. Skipping upload.\n') - return False - - if self.arguments.mode == 'force': - logger.warning('Distribution "%s" already exists. Removing.', basename) - self.api.remove_dist(self.username, meta.name, meta.version, basename) - continue - - if self.arguments.mode == 'interactive': - if bool_input(f'Distribution "{basename}" already exists. Would you like to replace it?'): - self.api.remove_dist(self.username, meta.name, meta.version, basename) - continue - logger.info('Not replacing distribution "%s"', basename) - return False - - logger.info( - ( - 'Distribution already exists. ' # pylint: disable=implicit-str-concat - 'Please use the -i/--interactive or --force or --skip options or `anaconda remove %s/%s/%s/%s`' - ), - self.username, meta.name, meta.version, basename, - ) - raise + logger.info( + ( + 'Distribution already exists. ' # pylint: disable=implicit-str-concat + 'Please use the -i/--interactive or --force or --skip options or `anaconda remove %s/%s/%s/%s`' + ), + self.username, meta.name, meta.version, basename, + ) + raise errors.Conflict(f'file {basename} already exists for package {meta.name} version {meta.version}', 409) - return False + def _upload_file(self, meta: PackageMeta) -> bool: + """Perform upload of a file after its metadata and related package and release are prepared.""" + basename: str = meta.file_attrs['basename'] + package_type: typing.Union[PackageType, str] = meta.file_attrs.pop('binstar_package_type', meta.package_type) + + stream: typing.BinaryIO + with open(meta.filename, 'rb') as stream: + result: typing.Mapping[str, typing.Any] = self.api.upload( + self.username, + meta.name, + meta.version, + basename, + stream, + package_type, + self.arguments.description, + dependencies=meta.file_attrs.get('dependencies'), + attrs=meta.file_attrs['attrs'], + channels=self.arguments.labels, + ) + + self.uploaded_packages.append({ + 'package_type': meta.package_type, + 'username': self.username, + 'name': meta.name, + 'version': meta.version, + 'basename': basename, + 'url': result.get('url', f'https://anaconda.org/{self.username}/{meta.name}'), + }) + self.__package_cache[meta.package_key].update(meta.package_type) + self.__release_cache[meta.release_key].update() + logger.info('Upload complete\n') + return True @staticmethod def detect_package_meta(filename: str, package_type: typing.Optional[PackageType] = None) -> detect.Meta: diff --git a/binstar_client/inspect_package/conda.py b/binstar_client/inspect_package/conda.py index 161d47bb..7743e4f5 100644 --- a/binstar_client/inspect_package/conda.py +++ b/binstar_client/inspect_package/conda.py @@ -101,8 +101,10 @@ def _load(filename, default=None): icon_b64 = data_uri_from(icon_path) subdir = get_subdir(index) - machine = index['arch'] - operatingsystem = os_map.get(index['platform'], index['platform']) + machine = index.get('arch', None) + platform = index.get('platform', None) + + operatingsystem = os_map.get(platform, platform) package_data = { 'name': index.pop('name'), diff --git a/binstar_client/scripts/cli.py b/binstar_client/scripts/cli.py index c3471b43..bc43b3d5 100644 --- a/binstar_client/scripts/cli.py +++ b/binstar_client/scripts/cli.py @@ -237,7 +237,7 @@ def _load_main_plugin() -> typing.Optional[typing.Callable[[], typing.Any]]: # e.g. in pyproject.toml, where my_plugin_library.cli.main is the callable entrypoint function # [project.entry-points."anaconda_cli.main"] # anaconda = "my_plugin_library.cli:main" - return plugin_mains[0].load() + return tuple(plugin_mains)[0].load() return None diff --git a/binstar_client/utils/appdirs.py b/binstar_client/utils/appdirs.py index cbc17d1e..40bda8ca 100644 --- a/binstar_client/utils/appdirs.py +++ b/binstar_client/utils/appdirs.py @@ -17,6 +17,10 @@ def __init__(self, root_path): def user_data_dir(self): return os.path.join(self.root_path, 'data') + @property + def user_config_dir(self): + return os.path.join(self.root_path, 'data') + @property def site_data_dir(self): return os.path.join(self.root_path, 'data') diff --git a/binstar_client/utils/config.py b/binstar_client/utils/config.py index 31761d9b..5caca039 100644 --- a/binstar_client/utils/config.py +++ b/binstar_client/utils/config.py @@ -36,7 +36,7 @@ if 'BINSTAR_CONFIG_DIR' in os.environ: dirs = EnvAppDirs(os.environ['BINSTAR_CONFIG_DIR']) - USER_CONFIG = os.path.join(dirs.user_data_dir, 'config.yaml') + USER_CONFIG = os.path.join(dirs.user_config_dir, 'config.yaml') else: dirs = PlatformDirs('binstar', 'ContinuumIO') # type: ignore USER_CONFIG = os.path.join(os.path.expanduser('~'), '.continuum', 'anaconda-client', 'config.yaml') @@ -109,7 +109,7 @@ def _missing_(cls, value: typing.Any) -> PackageType: dirs.site_data_dir, '/etc/anaconda-client/', '$CONDA_ROOT/etc/anaconda-client/', - dirs.user_data_dir, + dirs.user_config_dir, '~/.continuum/anaconda-client/', '$CONDA_PREFIX/etc/anaconda-client/', ) @@ -180,7 +180,7 @@ def get_binstar(args=None, cls=None): TOKEN_DIRS = [ - dirs.user_data_dir, + dirs.user_config_dir, os.path.join(os.path.dirname(USER_CONFIG), 'tokens'), ] TOKEN_DIR = TOKEN_DIRS[-1] diff --git a/tests/test_upload.py b/tests/test_upload.py index 8f4cc02c..5d96d64d 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -21,17 +21,17 @@ def test_upload_bad_package(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') registry.register(method='GET', path='/package/eggs/foo', content='{}', status=404) - content = {'package_types': ['conda']} - registry.register(method='POST', path='/package/eggs/foo', content=content, status=200) + registry.register(method='POST', path='/package/eggs/foo', content={'package_types': ['conda']}, status=200) registry.register(method='GET', path='/release/eggs/foo/0.1', content='{}') registry.register(method='GET', path='/dist/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=404, content='{}') - - content = {'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'} staging_response = registry.register( - method='POST', path='/stage/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', content=content) - + method='POST', + path='/stage/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', + content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, + ) registry.register(method='POST', path='/s3_url', status=201) registry.register(method='POST', path='/commit/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=200, content={}) + main(['--show-traceback', 'upload', data_dir('foo-0.1-0.tar.bz2')]) self.assertIsNotNone(json.loads(staging_response.req.body).get('sha256')) @@ -40,6 +40,7 @@ def test_upload_bad_package(self, registry): def test_upload_bad_package_no_register(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') + registry.register(method='GET', path='/dist/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=404) registry.register(method='GET', path='/package/eggs/foo', status=404) with self.assertRaises(errors.UserError): @@ -51,15 +52,14 @@ def test_upload_bad_package_no_register(self, registry): def test_upload_conda(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') - content = {'package_types': ['conda']} - registry.register(method='GET', path='/package/eggs/foo', content=content) + registry.register(method='GET', path='/dist/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=404) + registry.register(method='GET', path='/package/eggs/foo', content={'package_types': ['conda']}) registry.register(method='GET', path='/release/eggs/foo/0.1', content='{}') - - content = {'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'} staging_response = registry.register( - method='POST', path='/stage/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', content=content, + method='POST', + path='/stage/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', + content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, ) - registry.register(method='POST', path='/s3_url', status=201) registry.register(method='POST', path='/commit/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=200, content={}) @@ -72,15 +72,14 @@ def test_upload_conda(self, registry): def test_upload_conda_v2(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') - content = {'package_types': ['conda']} - registry.register(method='GET', path='/package/eggs/mock', content=content) + registry.register(method='GET', path='/dist/eggs/mock/2.0.0/osx-64/mock-2.0.0-py37_1000.conda', status=404) + registry.register(method='GET', path='/package/eggs/mock', content={'package_types': ['conda']}) registry.register(method='GET', path='/release/eggs/mock/2.0.0', content='{}') - - content = {'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'} staging_response = registry.register( - method='POST', path='/stage/eggs/mock/2.0.0/osx-64/mock-2.0.0-py37_1000.conda', content=content, + method='POST', + path='/stage/eggs/mock/2.0.0/osx-64/mock-2.0.0-py37_1000.conda', + content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, ) - registry.register(method='POST', path='/s3_url', status=201) registry.register( method='POST', path='/commit/eggs/mock/2.0.0/osx-64/mock-2.0.0-py37_1000.conda', status=200, content={}, @@ -95,19 +94,21 @@ def test_upload_conda_v2(self, registry): def test_upload_use_pkg_metadata(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') - content = {'package_types': ['conda']} - registry.register(method='GET', path='/package/eggs/mock', content=content) + registry.register(method='GET', path='/dist/eggs/mock/2.0.0/osx-64/mock-2.0.0-py37_1000.conda', status=404) + registry.register(method='GET', path='/package/eggs/mock', content={'package_types': ['conda']}) registry.register(method='GET', path='/release/eggs/mock/2.0.0', content='{}') registry.register(method='PATCH', path='/release/eggs/mock/2.0.0', content='{}') - - content = {'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'} staging_response = registry.register( - method='POST', path='/stage/eggs/mock/2.0.0/osx-64/mock-2.0.0-py37_1000.conda', content=content, + method='POST', + path='/stage/eggs/mock/2.0.0/osx-64/mock-2.0.0-py37_1000.conda', + content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, ) - registry.register(method='POST', path='/s3_url', status=201) registry.register( - method='POST', path='/commit/eggs/mock/2.0.0/osx-64/mock-2.0.0-py37_1000.conda', status=200, content={}, + method='POST', + path='/commit/eggs/mock/2.0.0/osx-64/mock-2.0.0-py37_1000.conda', + status=200, + content={}, ) main(['--show-traceback', 'upload', '--force-metadata-update', data_dir('mock-2.0.0-py37_1000.conda')]) @@ -119,18 +120,20 @@ def test_upload_use_pkg_metadata(self, registry): def test_upload_pypi(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') - content = {'package_types': ['pypi']} - registry.register(method='GET', path='/package/eggs/test-package34', content=content) + registry.register(method='GET', path='/dist/eggs/test-package34/0.3.1/test_package34-0.3.1.tar.gz', status=404) + registry.register(method='GET', path='/package/eggs/test-package34', content={'package_types': ['pypi']}) registry.register(method='GET', path='/release/eggs/test-package34/0.3.1', content='{}') - - content = {'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'} staging_response = registry.register( - method='POST', path='/stage/eggs/test-package34/0.3.1/test_package34-0.3.1.tar.gz', content=content, + method='POST', + path='/stage/eggs/test-package34/0.3.1/test_package34-0.3.1.tar.gz', + content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, ) - registry.register(method='POST', path='/s3_url', status=201) registry.register( - method='POST', path='/commit/eggs/test-package34/0.3.1/test_package34-0.3.1.tar.gz', status=200, content={}, + method='POST', + path='/commit/eggs/test-package34/0.3.1/test_package34-0.3.1.tar.gz', + status=200, + content={}, ) main(['--show-traceback', 'upload', data_dir('test_package34-0.3.1.tar.gz')]) @@ -142,18 +145,20 @@ def test_upload_pypi(self, registry): def test_upload_pypi_with_conda_package_name_allowed(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') - content = {'package_types': ['pypi']} - registry.register(method='GET', path='/package/eggs/test_package34', content=content) + registry.register(method='GET', path='/dist/eggs/test_package34/0.3.1/test_package34-0.3.1.tar.gz', status=404) + registry.register(method='GET', path='/package/eggs/test_package34', content={'package_types': ['pypi']}) registry.register(method='GET', path='/release/eggs/test_package34/0.3.1', content='{}') - - content = {'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'} staging_response = registry.register( - method='POST', path='/stage/eggs/test_package34/0.3.1/test_package34-0.3.1.tar.gz', content=content, + method='POST', + path='/stage/eggs/test_package34/0.3.1/test_package34-0.3.1.tar.gz', + content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, ) - registry.register(method='POST', path='/s3_url', status=201) registry.register( - method='POST', path='/commit/eggs/test_package34/0.3.1/test_package34-0.3.1.tar.gz', status=200, content={}, + method='POST', + path='/commit/eggs/test_package34/0.3.1/test_package34-0.3.1.tar.gz', + status=200, + content={}, ) # Pass -o to override the channel/package pypi package should go to @@ -192,18 +197,20 @@ def test_upload_pypi_with_random_name(self, registry): def test_upload_file(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') - content = {'package_types': ['file']} - registry.register(method='GET', path='/package/eggs/test-package34', content=content) + registry.register(method='GET', path='/dist/eggs/test-package34/0.3.1/test_package34-0.3.1.tar.gz', status=404) + registry.register(method='GET', path='/package/eggs/test-package34', content={'package_types': ['file']}) registry.register(method='GET', path='/release/eggs/test-package34/0.3.1', content='{}') - - content = {'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'} staging_response = registry.register( - method='POST', path='/stage/eggs/test-package34/0.3.1/test_package34-0.3.1.tar.gz', content=content, + method='POST', + path='/stage/eggs/test-package34/0.3.1/test_package34-0.3.1.tar.gz', + content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, ) - registry.register(method='POST', path='/s3_url', status=201) registry.register( - method='POST', path='/commit/eggs/test-package34/0.3.1/test_package34-0.3.1.tar.gz', status=200, content={}, + method='POST', + path='/commit/eggs/test-package34/0.3.1/test_package34-0.3.1.tar.gz', + status=200, + content={}, ) main([ @@ -216,15 +223,17 @@ def test_upload_file(self, registry): @urlpatch def test_upload_project(self, registry): - # there's redundant work between anaconda-client which - # checks auth and anaconda-project also checks auth; + # there's redundant work between anaconda-client which checks auth and anaconda-project also checks auth; # -project has no way to know it was already checked :-/ registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user/eggs', content='{"login": "eggs"}') registry.register(method='GET', path='/user', content='{"login": "eggs"}') registry.register(method='GET', path='/apps/eggs/projects/dog', content='{}') - stage_content = '{"post_url":"http://s3url.com/s3_url", "form_data":{"foo":"bar"}, "dist_id":"dist42"}' - registry.register(method='POST', path='/apps/eggs/projects/dog/stage', content=stage_content) + registry.register( + method='POST', + path='/apps/eggs/projects/dog/stage', + content='{"post_url":"http://s3url.com/s3_url", "form_data":{"foo":"bar"}, "dist_id":"dist42"}', + ) registry.register(method='POST', path='/s3_url', status=201) registry.register(method='POST', path='/apps/eggs/projects/dog/commit/dist42', content='{}') @@ -238,8 +247,11 @@ def test_upload_notebook_as_project(self, registry): registry.register(method='GET', path='/user/eggs', content='{"login": "eggs"}') registry.register(method='GET', path='/user', content='{"login": "eggs"}') registry.register(method='GET', path='/apps/eggs/projects/foo', content='{}') - stage_content = '{"post_url":"http://s3url.com/s3_url", "form_data":{"foo":"bar"}, "dist_id":"dist42"}' - registry.register(method='POST', path='/apps/eggs/projects/foo/stage', content=stage_content) + registry.register( + method='POST', + path='/apps/eggs/projects/foo/stage', + content='{"post_url":"http://s3url.com/s3_url", "form_data":{"foo":"bar"}, "dist_id":"dist42"}', + ) registry.register(method='POST', path='/s3_url', status=201) registry.register(method='POST', path='/apps/eggs/projects/foo/commit/dist42', content='{}') @@ -254,18 +266,20 @@ def test_upload_notebook_as_package(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') - content = {'package_types': ['ipynb']} - registry.register(method='GET', path='/package/eggs/foo', content=content) + registry.register(method='GET', path='/dist/eggs/foo/2022.05.19.1529/foo.ipynb', status=404) + registry.register(method='GET', path='/package/eggs/foo', content={'package_types': ['ipynb']}) registry.register(method='GET', path='/release/eggs/foo/{}'.format(mock_version), content='{}') - - content = {'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'} staging_response = registry.register( - method='POST', path='/stage/eggs/foo/{}/foo.ipynb'.format(mock_version), content=content, + method='POST', + path='/stage/eggs/foo/{}/foo.ipynb'.format(mock_version), + content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, ) - registry.register(method='POST', path='/s3_url', status=201) registry.register( - method='POST', path='/commit/eggs/foo/{}/foo.ipynb'.format(mock_version), status=200, content={}, + method='POST', + path='/commit/eggs/foo/{}/foo.ipynb'.format(mock_version), + status=200, + content={}, ) with unittest.mock.patch('binstar_client.inspect_package.ipynb.datetime') as mock_datetime: @@ -280,8 +294,11 @@ def test_upload_project_specifying_user(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user/alice', content='{"login": "alice"}') registry.register(method='GET', path='/apps/alice/projects/dog', content='{}') - stage_content = '{"post_url":"http://s3url.com/s3_url", "form_data":{"foo":"bar"}, "dist_id":"dist42"}' - registry.register(method='POST', path='/apps/alice/projects/dog/stage', content=stage_content) + registry.register( + method='POST', + path='/apps/alice/projects/dog/stage', + content='{"post_url":"http://s3url.com/s3_url", "form_data":{"foo":"bar"}, "dist_id":"dist42"}', + ) registry.register(method='POST', path='/s3_url', status=201) registry.register(method='POST', path='/apps/alice/projects/dog/commit/dist42', content='{}') @@ -293,13 +310,18 @@ def test_upload_project_specifying_user(self, registry): def test_upload_project_specifying_token(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register( - method='GET', path='/user/eggs', content='{"login": "eggs"}', + method='GET', + path='/user/eggs', + content='{"login": "eggs"}', expected_headers={'Authorization': 'token abcdefg'}, ) registry.register(method='GET', path='/user', content='{"login": "eggs"}') registry.register(method='GET', path='/apps/eggs/projects/dog', content='{}') - stage_content = '{"post_url":"http://s3url.com/s3_url", "form_data":{"foo":"bar"}, "dist_id":"dist42"}' - registry.register(method='POST', path='/apps/eggs/projects/dog/stage', content=stage_content) + registry.register( + method='POST', + path='/apps/eggs/projects/dog/stage', + content='{"post_url":"http://s3url.com/s3_url", "form_data":{"foo":"bar"}, "dist_id":"dist42"}', + ) registry.register(method='POST', path='/s3_url', status=201) registry.register(method='POST', path='/apps/eggs/projects/dog/commit/dist42', content='{}') @@ -313,68 +335,51 @@ def test_upload_interactive_no_overwrite(self, registry, bool_input): # regression test for #364 registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') - content = {'package_types': ['conda']} - registry.register(method='GET', path='/package/eggs/foo', content=content) - registry.register(method='GET', path='/release/eggs/foo/0.1', content='{}') - query_001 = registry.register(method='POST', path='/stage/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=409) + registry.register(method='GET', path='/dist/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', content='{}') bool_input.return_value = False # do not overwrite package main(['--show-traceback', 'upload', '-i', data_dir('foo-0.1-0.tar.bz2')]) registry.assertAllCalled() - self.assertIsNotNone(json.loads(query_001.req.body).get('sha256')) @urlpatch @unittest.mock.patch('binstar_client.commands.upload.bool_input') def test_upload_interactive_overwrite(self, registry, bool_input): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') - content = {'package_types': ['conda']} - registry.register(method='GET', path='/package/eggs/foo', content=content) + registry.register(method='GET', path='/dist/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', content='{}') + registry.register(method='DELETE', path='/dist/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', content='{}') + registry.register(method='GET', path='/package/eggs/foo', content={'package_types': ['conda']}) registry.register(method='GET', path='/release/eggs/foo/0.1', content='{}') - query_001 = registry.register(method='POST', path='/stage/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=409) - query_002 = None - - def allow_upload(): - nonlocal query_002 - query_002 = registry.register( - method='POST', path='/stage/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', - content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, - ) - registry.register(method='POST', path='/s3_url', status=201) - registry.register( - method='POST', path='/commit/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=200, content={}, - ) - - registry.register( - method='DELETE', path='/dist/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', content='{}', - side_effect=allow_upload, + staging_response = registry.register( + method='POST', + path='/stage/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', + content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, ) + registry.register(method='POST', path='/s3_url', status=201) + registry.register(method='POST', path='/commit/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=200, content={}) bool_input.return_value = True main(['--show-traceback', 'upload', '-i', data_dir('foo-0.1-0.tar.bz2')]) registry.assertAllCalled() - self.assertIsNotNone(json.loads(query_001.req.body).get('sha256')) - self.assertIsNotNone(query_002) - self.assertIsNotNone(json.loads(query_002.req.body).get('sha256')) + self.assertIsNotNone(json.loads(staging_response.req.body).get('sha256')) @urlpatch def test_upload_private_package(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') + registry.register(method='GET', path='/dist/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=404) registry.register(method='GET', path='/package/eggs/foo', content='{}', status=404) - content = {'package_types': ['conda']} - registry.register(method='POST', path='/package/eggs/foo', content=content, status=200) + registry.register(method='POST', path='/package/eggs/foo', content={'package_types': ['conda']}, status=200) registry.register(method='GET', path='/release/eggs/foo/0.1', content='{}') - - content = {'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'} staging_response = registry.register( - method='POST', path='/stage/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', content=content, + method='POST', + path='/stage/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', + content={'post_url': 'http://s3url.com/s3_url', 'form_data': {}, 'dist_id': 'dist_id'}, ) - registry.register(method='POST', path='/s3_url', status=201) registry.register(method='POST', path='/commit/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=200, content={}) @@ -387,9 +392,12 @@ def test_upload_private_package(self, registry): def test_upload_private_package_not_allowed(self, registry): registry.register(method='HEAD', path='/', status=200) registry.register(method='GET', path='/user', content='{"login": "eggs"}') + registry.register(method='GET', path='/dist/eggs/foo/0.1/osx-64/foo-0.1-0.tar.bz2', status=404) registry.register(method='GET', path='/package/eggs/foo', content='{}', status=404) registry.register( - method='POST', path='/package/eggs/foo', content='{"error": "You can not create a private package."}', + method='POST', + path='/package/eggs/foo', + content='{"error": "You can not create a private package."}', status=400, )