Skip to content

Commit

Permalink
Merge pull request #694 from Anaconda-Platform/release/1.12.2
Browse files Browse the repository at this point in the history
Release 1.12.2
  • Loading branch information
vshevchenko-anaconda authored Nov 16, 2023
2 parents 3f7c2d1 + 5705f76 commit d9849e3
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 360 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

We [keep a changelog.](http://keepachangelog.com/)

## 1.12.2

### Tickets closed

* AC-192 - SPIKE: Remove unnecessary /user calls
* AC-194 - Check token before calling /user endpoint
* AC-200 - Replacing vendored appdirs with platformdirs

### Pull requests merged

* [PR 693](https://github.com/Anaconda-Platform/anaconda-client/pull/693) - AC-200: replace appdirs with platformdirs
* [PR 691](https://github.com/Anaconda-Platform/anaconda-client/pull/691) - Support Python 3.12
* [PR 688](https://github.com/Anaconda-Platform/anaconda-client/pull/688) - AC-194: check token before making /user call
* [PR 680](https://github.com/Anaconda-Platform/anaconda-client/pull/680) - use conda.gateways.anaconda_client for tokens possible

## 1.12.1 - 2023-09-13

### Tickets closed
Expand Down
2 changes: 1 addition & 1 deletion binstar_client/__about__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

__all__ = ['__version__']

__version__ = '1.12.1'
__version__ = '1.12.2'
7 changes: 5 additions & 2 deletions binstar_client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,12 @@ def user(self, login=None):
this method will return the information of the authenticated user.
"""
if login:
url = '%s/user/%s' % (self.domain, login)
url = f'{self.domain}/user/{login}'
elif self.token:
url = f'{self.domain}/user'
else:
url = '%s/user' % (self.domain)
raise errors.Unauthorized(
'Authentication token is missing. Please, use `anaconda login` to reauthenticate.', 401)

res = self.session.get(url, verify=self.session.verify)
self._check_response(res)
Expand Down
5 changes: 1 addition & 4 deletions binstar_client/errors.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring

from clyent.errors import ClyentError


class BinstarError(ClyentError):
class BinstarError(Exception):

def __init__(self, *args):
super().__init__(*args)
Expand Down
113 changes: 104 additions & 9 deletions binstar_client/scripts/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@

import argparse
from importlib import metadata
import json
import logging
import os
import pkgutil
import sys
import types
import typing

from clyent import add_subparser_modules

from binstar_client import __version__
from binstar_client import commands
from binstar_client import errors
Expand All @@ -26,6 +26,13 @@
logger = logging.getLogger('binstar')


def _get_entry_points(group: str) -> typing.List[metadata.EntryPoint]:
# The API was changed in Python 3.10, see https://docs.python.org/3/library/importlib.metadata.html#entry-points
if sys.version_info.major == 3 and sys.version_info.minor < 10:
return metadata.entry_points().get(group, [])
return metadata.entry_points().select(group=group) # type: ignore


def file_or_token(value: str) -> str:
"""
Retrieve a token from input.
Expand All @@ -49,6 +56,99 @@ def file_or_token(value: str) -> str:
return value


def _json_action(action):
# pylint: disable=protected-access # intentional access of argparse object members
a_data = dict(action._get_kwargs())

if a_data.get('help'):
a_data['help'] = a_data['help'] % a_data

if isinstance(action, argparse._SubParsersAction):
a_data.pop('choices', None)
choices = {}
for choice in action._get_subactions():
choices[choice.dest] = choice.help
a_data['choices'] = choices

reg = {value: key for key, value in action.container._registries['action'].items()}
a_data['action'] = reg.get(type(action), type(action).__name__)
if a_data['action'] == 'store' and not a_data.get('metavar'):
a_data['metavar'] = action.dest.upper()

a_data.pop('type', None)
a_data.pop('default', None)

return a_data


def _json_group(group):
# pylint: disable=protected-access # intentional access of argparse object members
grp_data = {
'description': group.description,
'title': group.title,
'actions': [_json_action(action) for action in group._group_actions if action.help != argparse.SUPPRESS],
}

if group._action_groups:
grp_data['groups'] = [_json_group(group) for group in group._action_groups]

return grp_data


class _JSONHelp(argparse.Action):
# pylint: disable-next=redefined-builtin
def __init__(self, option_strings, dest, nargs=0, help=argparse.SUPPRESS, **kwargs):
argparse.Action.__init__(self, option_strings, dest, nargs=nargs, help=help, **kwargs)

def __call__(self, parser, namespace, values, option_string=None):
# pylint: disable=protected-access # intentional access of argparse object members
self.nargs = 0
docs = {
'prog': parser.prog,
'usage': parser.format_usage()[7:],
'description': parser.description,
'epilog': parser.epilog,
}

docs['groups'] = []
for group in parser._action_groups:
if group._group_actions:
docs['groups'].append(_json_group(group))

json.dump(docs, sys.stdout, indent=2)
raise SystemExit(0)


def _get_sub_command_names(module):
return [name for _, name, _ in pkgutil.iter_modules([os.path.dirname(module.__file__)]) if not name.startswith('_')]


def _get_sub_commands(module):
names = _get_sub_command_names(module)
this_module = __import__(module.__package__ or module.__name__, fromlist=names)

for name in names:
yield getattr(this_module, name)


def _add_subparser_modules(parser, module=None, entry_point_name=None):

subparsers = parser.add_subparsers(title='Commands', metavar='')

if module: # LOAD sub parsers from module
for command_module in _get_sub_commands(module):
command_module.add_parser(subparsers)

if entry_point_name: # LOAD sub parsers from setup.py entry_point
for entry_point in _get_entry_points(entry_point_name):
add_parser = entry_point.load()
add_parser(subparsers)

for key, sub_parser in subparsers.choices.items():
sub_parser.set_defaults(sub_command_name=key)
sub_parser.add_argument('--json-help', action=_JSONHelp)


def binstar_main(
sub_command_module: types.ModuleType,
args: typing.Optional[typing.Sequence[str]] = None,
Expand Down Expand Up @@ -89,7 +189,7 @@ def binstar_main(
'-V', '--version', action='version', version=f'%(prog)s Command line client (version {__version__})',
)

add_subparser_modules(parser, sub_command_module, 'conda_server.subcommand')
_add_subparser_modules(parser, sub_command_module, 'conda_server.subcommand')

arguments: argparse.Namespace = parser.parse_args(args)

Expand Down Expand Up @@ -123,12 +223,7 @@ def _load_main_plugin() -> typing.Optional[typing.Callable[[], typing.Any]]:
"""Allow loading a new CLI main entrypoint via plugin mechanisms. There can only be one."""
plugin_group_name: typing.Final[str] = 'anaconda_cli.main'

# The API was changed in Python 3.10, see https://docs.python.org/3/library/importlib.metadata.html#entry-points
plugin_mains: typing.List[metadata.EntryPoint]
if sys.version_info.major == 3 and sys.version_info.minor < 10:
plugin_mains = metadata.entry_points().get(plugin_group_name, [])
else:
plugin_mains = metadata.entry_points().select(group=plugin_group_name) # type: ignore
plugin_mains: typing.List[metadata.EntryPoint] = _get_entry_points(plugin_group_name)

if len(plugin_mains) > 1:
raise EnvironmentError(
Expand Down
Loading

0 comments on commit d9849e3

Please sign in to comment.