1
0
mirror of https://github.com/httpie/cli.git synced 2024-11-28 08:38:44 +02:00

Add httpie cli plugins in favor of the new cli namespace. (#1320)

* Add `httpie cli plugins` in favor of the new cli namespace.

* Separate each task to individual modules.

* Move httpie.manager.plugins to httpie.manager.tasks.plugins

Co-authored-by: Jakub Roztocil <jakub@roztocil.co>
This commit is contained in:
Batuhan Taskaya 2022-04-03 16:06:42 +03:00 committed by GitHub
parent 33ea977b64
commit c157948531
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 120 additions and 100 deletions

View File

@ -5,8 +5,10 @@ This project adheres to [Semantic Versioning](https://semver.org/).
## [3.1.1.dev0](https://github.com/httpie/httpie/compare/3.1.0...HEAD) (Unreleased) ## [3.1.1.dev0](https://github.com/httpie/httpie/compare/3.1.0...HEAD) (Unreleased)
- Changed `httpie plugins` to the new `httpie cli` namespace as `httpie cli plugins` (`httpie plugins` continues to work as a hidden alias). ([#1320](https://github.com/httpie/httpie/issues/1320))
- Fixed redundant creation of `Content-Length` header on `OPTIONS` requests. ([#1310](https://github.com/httpie/httpie/issues/1310)) - Fixed redundant creation of `Content-Length` header on `OPTIONS` requests. ([#1310](https://github.com/httpie/httpie/issues/1310))
## [3.1.0](https://github.com/httpie/httpie/compare/3.0.2...3.1.0) (2022-03-08) ## [3.1.0](https://github.com/httpie/httpie/compare/3.0.2...3.1.0) (2022-03-08)
- **SECURITY** Fixed the [vulnerability](https://github.com/httpie/httpie/security/advisories/GHSA-9w4w-cpc8-h2fq) that caused exposure of cookies on redirects to third party hosts. ([#1312](https://github.com/httpie/httpie/pull/1312)) - **SECURITY** Fixed the [vulnerability](https://github.com/httpie/httpie/security/advisories/GHSA-9w4w-cpc8-h2fq) that caused exposure of cookies on redirects to third party hosts. ([#1312](https://github.com/httpie/httpie/pull/1312))

View File

@ -2347,7 +2347,7 @@ However, it is not recommended modifying the default behavior in a way that woul
#### `plugins_dir` #### `plugins_dir`
The directory where the plugins will be installed. HTTPie needs to have read/write access on that directory, since The directory where the plugins will be installed. HTTPie needs to have read/write access on that directory, since
`httpie plugins install` will download new plugins to there. See [plugin manager](#plugin-manager) for more information. `httpie cli plugins install` will download new plugins to there. See [plugin manager](#plugin-manager) for more information.
### Un-setting previously specified options ### Un-setting previously specified options
@ -2425,7 +2425,7 @@ $ httpie cli export-args | jq '"Program: " + .spec.name + ", Version: " + .vers
"Program: http, Version: 0.0.1a0" "Program: http, Version: 0.0.1a0"
``` ```
### `httpie plugins` #### `httpie cli plugins`
`plugins` interface is a very simple plugin manager for installing, listing and uninstalling HTTPie plugins. `plugins` interface is a very simple plugin manager for installing, listing and uninstalling HTTPie plugins.
@ -2436,13 +2436,13 @@ plugin installations on every installation method.
By default, the plugins (and their missing dependencies) will be stored under the configuration directory, By default, the plugins (and their missing dependencies) will be stored under the configuration directory,
but this can be modified through `plugins_dir` variable on the config. but this can be modified through `plugins_dir` variable on the config.
#### `httpie plugins install` ##### `httpie cli plugins install`
For installing plugins from [PyPI](https://pypi.org/) or from local paths, `httpie plugins install` For installing plugins from [PyPI](https://pypi.org/) or from local paths, `httpie cli plugins install`
can be used. can be used.
```bash ```bash
$ httpie plugins install httpie-plugin $ httpie cli plugins install httpie-plugin
Installing httpie-plugin... Installing httpie-plugin...
Successfully installed httpie-plugin-1.0.2 Successfully installed httpie-plugin-1.0.2
``` ```
@ -2450,12 +2450,12 @@ Successfully installed httpie-plugin-1.0.2
> Tip: Generally HTTPie plugins start with `httpie-` prefix. Try searching for it on [PyPI](https://pypi.org/search/?q=httpie-) > Tip: Generally HTTPie plugins start with `httpie-` prefix. Try searching for it on [PyPI](https://pypi.org/search/?q=httpie-)
> to find out all plugins from the community. > to find out all plugins from the community.
#### `httpie plugins list` ##### `httpie cli plugins list`
List all installed plugins. List all installed plugins.
```bash ```bash
$ httpie plugins list $ httpie cli plugins list
httpie_plugin (1.0.2) httpie_plugin (1.0.2)
httpie_plugin (httpie.plugins.auth.v1) httpie_plugin (httpie.plugins.auth.v1)
httpie_plugin_2 (1.0.6) httpie_plugin_2 (1.0.6)
@ -2465,21 +2465,21 @@ httpie_converter (1.0.0)
httpie_konsole_konverter (httpie.plugins.converter.v1) httpie_konsole_konverter (httpie.plugins.converter.v1)
``` ```
#### `httpie plugins upgrade` ##### `httpie cli plugins upgrade`
For upgrading already installed plugins, use `httpie plugins upgrade`. For upgrading already installed plugins, use `httpie plugins upgrade`.
```bash ```bash
$ httpie plugins upgrade httpie-plugin $ httpie cli plugins upgrade httpie-plugin
``` ```
#### `httpie plugins uninstall` ##### `httpie cli plugins uninstall`
Uninstall plugins from the isolated plugins directory. If the plugin is not installed Uninstall plugins from the isolated plugins directory. If the plugin is not installed
through `httpie plugins install`, it won’t uninstall it. through `httpie cli plugins install`, it won’t uninstall it.
```bash ```bash
$ httpie plugins uninstall httpie-plugin $ httpie cli plugins uninstall httpie-plugin
``` ```
## Meta ## Meta

View File

@ -12,37 +12,6 @@ CLI_SESSION_UPGRADE_FLAGS = [
] ]
COMMANDS = { COMMANDS = {
'plugins': {
'help': 'Manage HTTPie plugins.',
'install': [
'Install the given targets from PyPI '
'or from a local paths.',
{
'dest': 'targets',
'nargs': '+',
'help': 'targets to install'
}
],
'upgrade': [
'Upgrade the given plugins',
{
'dest': 'targets',
'nargs': '+',
'help': 'targets to upgrade'
}
],
'uninstall': [
'Uninstall the given HTTPie plugins.',
{
'dest': 'targets',
'nargs': '+',
'help': 'targets to install'
}
],
'list': [
'List all installed HTTPie plugins.'
],
},
'cli': { 'cli': {
'help': 'Manage HTTPie for Terminal', 'help': 'Manage HTTPie for Terminal',
'export-args': [ 'export-args': [
@ -82,6 +51,39 @@ COMMANDS = {
} }
COMMANDS['plugins'] = COMMANDS['cli']['plugins'] = {
'help': 'Manage HTTPie plugins.',
'install': [
'Install the given targets from PyPI '
'or from a local paths.',
{
'dest': 'targets',
'nargs': '+',
'help': 'targets to install'
}
],
'upgrade': [
'Upgrade the given plugins',
{
'dest': 'targets',
'nargs': '+',
'help': 'targets to upgrade'
}
],
'uninstall': [
'Uninstall the given HTTPie plugins.',
{
'dest': 'targets',
'nargs': '+',
'help': 'targets to install'
}
],
'list': [
'List all installed HTTPie plugins.'
],
}
def missing_subcommand(*args) -> str: def missing_subcommand(*args) -> str:
base = COMMANDS base = COMMANDS
for arg in args: for arg in args:

View File

@ -2,7 +2,6 @@ import argparse
from typing import Optional from typing import Optional
from httpie.context import Environment from httpie.context import Environment
from httpie.manager.plugins import PluginInstaller
from httpie.status import ExitStatus from httpie.status import ExitStatus
from httpie.manager.cli import missing_subcommand, parser from httpie.manager.cli import missing_subcommand, parser
from httpie.manager.tasks import CLI_TASKS from httpie.manager.tasks import CLI_TASKS
@ -36,8 +35,7 @@ def program(args: argparse.Namespace, env: Environment) -> ExitStatus:
parser.error(MSG_NAKED_INVOCATION) parser.error(MSG_NAKED_INVOCATION)
if args.action == 'plugins': if args.action == 'plugins':
plugins = PluginInstaller(env, debug=args.debug) return dispatch_cli_task(env, args.action, args)
return plugins.run(args.plugins_action, args)
elif args.action == 'cli': elif args.action == 'cli':
return dispatch_cli_task(env, args.cli_action, args) return dispatch_cli_task(env, args.cli_action, args)

View File

@ -0,0 +1,9 @@
from httpie.manager.tasks.sessions import cli_sessions
from httpie.manager.tasks.export_args import cli_export_args
from httpie.manager.tasks.plugins import cli_plugins
CLI_TASKS = {
'sessions': cli_sessions,
'export-args': cli_export_args,
'plugins': cli_plugins,
}

View File

@ -0,0 +1,27 @@
import argparse
import json
from httpie.cli.definition import options
from httpie.cli.options import to_data
from httpie.output.writer import write_raw_data
from httpie.status import ExitStatus
from httpie.context import Environment
FORMAT_TO_CONTENT_TYPE = {
'json': 'application/json'
}
def cli_export_args(env: Environment, args: argparse.Namespace) -> ExitStatus:
if args.format == 'json':
data = json.dumps(to_data(options))
else:
raise NotImplementedError(f'Unexpected format value: {args.format}')
write_raw_data(
env,
data,
stream_kwargs={'mime_overwrite': FORMAT_TO_CONTENT_TYPE[args.format]},
)
return ExitStatus.SUCCESS

View File

@ -1,18 +1,18 @@
import argparse import argparse
import os import os
import re
import shutil
import subprocess import subprocess
import sys import sys
import textwrap import textwrap
import re
import shutil
from collections import defaultdict from collections import defaultdict
from contextlib import suppress from contextlib import suppress
from pathlib import Path from pathlib import Path
from typing import Tuple, Optional, List from typing import List, Optional, Tuple
from httpie.manager.cli import parser, missing_subcommand from httpie.compat import get_dist_name, importlib_metadata
from httpie.compat import importlib_metadata, get_dist_name
from httpie.context import Environment from httpie.context import Environment
from httpie.manager.cli import missing_subcommand, parser
from httpie.status import ExitStatus from httpie.status import ExitStatus
from httpie.utils import as_site from httpie.utils import as_site
@ -248,3 +248,14 @@ class PluginInstaller:
status = self.list() status = self.list()
return status or ExitStatus.SUCCESS return status or ExitStatus.SUCCESS
def cli_plugins(env: Environment, args: argparse.Namespace) -> ExitStatus:
plugins = PluginInstaller(env, debug=args.debug)
try:
action = args.cli_plugins_action
except AttributeError:
action = args.plugins_action
return plugins.run(action, args)

View File

@ -1,5 +1,5 @@
import argparse import argparse
from typing import TypeVar, Callable, Tuple from typing import Tuple
from httpie.sessions import SESSIONS_DIR_NAME, get_httpie_session from httpie.sessions import SESSIONS_DIR_NAME, get_httpie_session
from httpie.status import ExitStatus from httpie.status import ExitStatus
@ -7,19 +7,7 @@ from httpie.context import Environment
from httpie.legacy import cookie_format as legacy_cookies from httpie.legacy import cookie_format as legacy_cookies
from httpie.manager.cli import missing_subcommand, parser from httpie.manager.cli import missing_subcommand, parser
T = TypeVar('T')
CLI_TASKS = {}
def task(name: str) -> Callable[[T], T]:
def wrapper(func: T) -> T:
CLI_TASKS[name] = func
return func
return wrapper
@task('sessions')
def cli_sessions(env: Environment, args: argparse.Namespace) -> ExitStatus: def cli_sessions(env: Environment, args: argparse.Namespace) -> ExitStatus:
action = args.cli_sessions_action action = args.cli_sessions_action
if action is None: if action is None:
@ -114,28 +102,3 @@ def cli_upgrade_all_sessions(env: Environment, args: argparse.Namespace) -> Exit
session_name=session_name session_name=session_name
) )
return status return status
FORMAT_TO_CONTENT_TYPE = {
'json': 'application/json'
}
@task('export-args')
def cli_export(env: Environment, args: argparse.Namespace) -> ExitStatus:
import json
from httpie.cli.definition import options
from httpie.cli.options import to_data
from httpie.output.writer import write_raw_data
if args.format == 'json':
data = json.dumps(to_data(options))
else:
raise NotImplementedError(f'Unexpected format value: {args.format}')
write_raw_data(
env,
data,
stream_kwargs={'mime_overwrite': FORMAT_TO_CONTENT_TYPE[args.format]},
)
return ExitStatus.SUCCESS

View File

@ -5,8 +5,9 @@ from tests.utils.plugins_cli import parse_listing
@pytest.mark.requires_installation @pytest.mark.requires_installation
def test_plugins_installation(httpie_plugins_success, interface, dummy_plugin): @pytest.mark.parametrize('cli_mode', [True, False])
lines = httpie_plugins_success('install', dummy_plugin.path) def test_plugins_installation(httpie_plugins_success, interface, dummy_plugin, cli_mode):
lines = httpie_plugins_success('install', dummy_plugin.path, cli_mode=cli_mode)
assert lines[0].startswith( assert lines[0].startswith(
f'Installing {dummy_plugin.path}' f'Installing {dummy_plugin.path}'
) )
@ -28,8 +29,9 @@ def test_plugin_installation_with_custom_config(httpie_plugins_success, interfac
@pytest.mark.requires_installation @pytest.mark.requires_installation
def test_plugins_listing(httpie_plugins_success, interface, dummy_plugin): @pytest.mark.parametrize('cli_mode', [True, False])
httpie_plugins_success('install', dummy_plugin.path) def test_plugins_listing(httpie_plugins_success, interface, dummy_plugin, cli_mode):
httpie_plugins_success('install', dummy_plugin.path, cli_mode=cli_mode)
data = parse_listing(httpie_plugins_success('list')) data = parse_listing(httpie_plugins_success('list'))
assert data == { assert data == {
@ -50,9 +52,10 @@ def test_plugins_listing_multiple(interface, httpie_plugins_success, dummy_plugi
@pytest.mark.requires_installation @pytest.mark.requires_installation
def test_plugins_uninstall(interface, httpie_plugins_success, dummy_plugin): @pytest.mark.parametrize('cli_mode', [True, False])
httpie_plugins_success('install', dummy_plugin.path) def test_plugins_uninstall(interface, httpie_plugins_success, dummy_plugin, cli_mode):
httpie_plugins_success('uninstall', dummy_plugin.name) httpie_plugins_success('install', dummy_plugin.path, cli_mode=cli_mode)
httpie_plugins_success('uninstall', dummy_plugin.name, cli_mode=cli_mode)
assert not interface.is_installed(dummy_plugin.name) assert not interface.is_installed(dummy_plugin.name)

View File

@ -208,12 +208,17 @@ def httpie_plugins(interface):
from tests.utils import httpie from tests.utils import httpie
from httpie.plugins.registry import plugin_manager from httpie.plugins.registry import plugin_manager
def runner(*args): def runner(*args, cli_mode: bool = True):
args = list(args)
if cli_mode:
args.insert(0, 'cli')
args.insert(cli_mode, 'plugins')
# Prevent installed plugins from showing up. # Prevent installed plugins from showing up.
original_plugins = plugin_manager.copy() original_plugins = plugin_manager.copy()
clean_sys_path = set(sys.path).difference(site.getsitepackages()) clean_sys_path = set(sys.path).difference(site.getsitepackages())
with patch('sys.path', list(clean_sys_path)): with patch('sys.path', list(clean_sys_path)):
response = httpie('plugins', *args, env=interface.environment) response = httpie(*args, env=interface.environment)
plugin_manager.clear() plugin_manager.clear()
plugin_manager.extend(original_plugins) plugin_manager.extend(original_plugins)
return response return response
@ -223,8 +228,8 @@ def httpie_plugins(interface):
@pytest.fixture @pytest.fixture
def httpie_plugins_success(httpie_plugins): def httpie_plugins_success(httpie_plugins):
def runner(*args): def runner(*args, cli_mode: bool = True):
response = httpie_plugins(*args) response = httpie_plugins(*args, cli_mode=True)
assert response.exit_status == ExitStatus.SUCCESS assert response.exit_status == ExitStatus.SUCCESS
return response.splitlines() return response.splitlines()
return runner return runner