You've already forked httpie-cli
mirror of
https://github.com/httpie/cli.git
synced 2025-07-15 01:34:27 +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:
@ -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))
|
||||||
|
@ -2347,7 +2347,7 @@ However, it is not recommended modifying the default behavior in a way that woul
|
|||||||
*) echo 'Other Error!' ;;
|
*) echo 'Other Error!' ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
### Best practices
|
### Best practices
|
||||||
|
|
||||||
@ -2425,7 +2425,7 @@ $ httpie cli export-args | jq '"Program: " + .spec.name + ", Version: " + .vers
|
|||||||
|
|
||||||
##### `httpie cli plugins list`
|
##### `httpie cli plugins list`
|
||||||
|
|
||||||
List all installed plugins.
|
List all installed plugins.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ httpie cli plugins list
|
$ httpie cli plugins list
|
||||||
@ -2436,13 +2436,13 @@ plugin installations on every installation method.
|
|||||||
httpie_converter (1.0.0)
|
httpie_converter (1.0.0)
|
||||||
httpie_iterm_converter (httpie.plugins.converter.v1)
|
httpie_iterm_converter (httpie.plugins.converter.v1)
|
||||||
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 cli plugins upgrade httpie-plugin
|
$ httpie cli plugins upgrade httpie-plugin
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -2450,12 +2450,12 @@ Successfully installed httpie-plugin-1.0.2
|
|||||||
|
|
||||||
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 cli plugins install`, it won’t uninstall it.
|
through `httpie cli plugins install`, it won’t uninstall it.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ httpie cli plugins uninstall httpie-plugin
|
$ httpie cli plugins uninstall httpie-plugin
|
||||||
```
|
```
|
||||||
|
|
||||||
## Meta
|
## Meta
|
||||||
|
|
||||||
### Interface design
|
### Interface design
|
||||||
|
|
||||||
@ -2465,21 +2465,21 @@ httpie_converter (1.0.0)
|
|||||||
For example, compare this HTTP request:
|
For example, compare this HTTP request:
|
||||||
|
|
||||||
```http
|
```http
|
||||||
POST /post HTTP/1.1
|
POST /post HTTP/1.1
|
||||||
Host: pie.dev
|
Host: pie.dev
|
||||||
X-API-Key: 123
|
X-API-Key: 123
|
||||||
User-Agent: Bacon/1.0
|
User-Agent: Bacon/1.0
|
||||||
Content-Type: application/x-www-form-urlencoded
|
Content-Type: application/x-www-form-urlencoded
|
||||||
|
|
||||||
name=value&name2=value2
|
name=value&name2=value2
|
||||||
```
|
```
|
||||||
|
|
||||||
with the HTTPie command that sends it:
|
with the HTTPie command that sends it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ http -f POST pie.dev/post \
|
$ http -f POST pie.dev/post \
|
||||||
X-API-Key:123 \
|
X-API-Key:123 \
|
||||||
User-Agent:Bacon/1.0 \
|
User-Agent:Bacon/1.0 \
|
||||||
name=value \
|
name=value \
|
||||||
name2=value2
|
name2=value2
|
||||||
```
|
```
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
9
httpie/manager/tasks/__init__.py
Normal file
9
httpie/manager/tasks/__init__.py
Normal 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,
|
||||||
|
}
|
27
httpie/manager/tasks/export_args.py
Normal file
27
httpie/manager/tasks/export_args.py
Normal 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
|
@ -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)
|
@ -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
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user