Compare commits

...

17 Commits

Author SHA1 Message Date
42af2f786f v2.6.0 (#1182)
[skip ci]
2021-10-14 10:36:39 +02:00
a65771e271 Add a script that lists all contributors to a release (#1181)
* Add a script that lists all contributors to a release

We will keep a contributors database (simple JSON file) where
each entry is a contributor (either a committer, either an issue reporter,
either both) with some nicknames (GitHub, and Twitter).
The file will be used to craft credits on our release blog posts and to ping
them on Twitter.

* Add templates

* Missing docstring

* Clean-up

* Tweak
2021-10-14 10:33:14 +02:00
7b683d4b57 Update CHANGELOG.md 2021-10-13 23:37:40 +02:00
a15fd6f966 Add --response=mime and --response=charset docs (#1179)
* Add the "display encoding" section in the docs

* Remove repetition

* `--response=mime` / `--response=charset` docs

* Cleanup

* Cleanup

* Cleanup

Co-authored-by: Jakub Roztocil <jakub@roztocil.co>
2021-10-13 23:32:46 +02:00
19691bba68 Packaging documentation tweaks 2021-10-11 17:42:29 +02:00
344491ba8e Tweak the Chocolatey package installation file 2021-10-11 10:07:24 +02:00
9f6fa090df Auto-update install docs
Via .github/workflows/docs-update-install.yml
2021-10-10 18:58:57 +00:00
59f4ef03cc Remove macOS/Snap
snapd is not available on macOS yet
2021-10-10 20:58:05 +02:00
ef92e2a74a Auto-update install docs
Via .github/workflows/docs-update-install.yml
2021-10-10 18:46:46 +00:00
1171984ec2 Better links for snap on macos 2021-10-10 20:45:53 +02:00
ce9746b1f8 Auto-update install docs
Via .github/workflows/docs-update-install.yml
2021-10-10 18:25:59 +00:00
6b99e1c932 Link GitHub action file in generated commit 2021-10-10 20:25:12 +02:00
7d418aecd0 Auto-update installation instructions in the docs 2021-10-10 18:18:39 +00:00
459cdfcf53 Tweak install docs template
Shorten setup, add missing comma
2021-10-10 20:17:49 +02:00
ab8512f96c Add --compress documentation (#1173)
* Add --compress documentation

* Apply suggestions from code review

Co-authored-by: Jakub Roztocil <jakub@roztocil.co>

* Update docs/README.md

* Update docs/README.md

Co-authored-by: Jakub Roztocil <jakub@roztocil.co>
2021-10-08 18:38:40 +02:00
6befaf9067 Added the ability to silence warnings via double -q or --quiet (#1175)
* change behavior of '--quiet' to silence errors and warnings when passed twice together with '--check-status'

* Apply suggestions from code review

Co-authored-by: Jakub Roztocil <jakub@roztocil.co>

* remove header, trailing comma, rename constant and variable

* fix flags for tests

* [skip ci] Update ticket number

Co-authored-by: Dave <d.kreeft@outlook.com>
Co-authored-by: Jakub Roztocil <jakub@roztocil.co>
Co-authored-by: Mickaël Schoentgen <contact@tiger-222.fr>
2021-10-08 14:18:11 +02:00
1b7f74c2b2 Add a workflow to control Snap publications (#1176) 2021-10-08 11:24:24 +02:00
19 changed files with 734 additions and 137 deletions

View File

@ -3,6 +3,7 @@ on:
branches:
- master
paths:
- .github/workflows/docs-update-install.yml
- docs/installation/*
# Allow to call the workflow manually
@ -21,6 +22,10 @@ jobs:
- uses: Automattic/action-commit-to-branch@master
with:
branch: master
commit_message: Auto-update installation instructions in the docs
commit_message: |
Auto-update install docs
Via .github/workflows/docs-update-install.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

22
.github/workflows/release-snap.yml vendored Normal file
View File

@ -0,0 +1,22 @@
on:
workflow_dispatch:
inputs:
branch:
description: "The branch, tag or SHA to release from"
required: true
default: "master"
jobs:
snap:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.branch }}
- uses: snapcore/action-build@v1
id: build
- uses: snapcore/action-publish@v1
with:
store_login: ${{ secrets.SNAP_STORE_LOGIN }}
snap: ${{ steps.build.outputs.snap }}
release: edge

View File

@ -17,6 +17,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.inputs.branch }}
- uses: actions/setup-python@v2
with:
python-version: 3.9

View File

@ -3,14 +3,15 @@
This document records all notable changes to [HTTPie](https://httpie.io).
This project adheres to [Semantic Versioning](https://semver.org/).
## [2.6.0.dev0](https://github.com/httpie/httpie/compare/2.5.0...master) (unreleased)
## [2.6.0](https://github.com/httpie/httpie/compare/2.5.0...2.6.0) (2021-10-14)
- Added support for formatting & coloring of JSON bodies preceded by non-JSON data (e.g., an XXSI prefix). ([#1130](https://github.com/httpie/httpie/issues/1130))
- Added charset auto-detection when `Content-Type` doesnt include it. ([#1110](https://github.com/httpie/httpie/issues/1110), [#1168](https://github.com/httpie/httpie/issues/1168))
- Added `--response-charset` to allow overriding the response encoding for terminal display purposes. ([#1168](https://github.com/httpie/httpie/issues/1168))
- Added `--response-mime` to allow overriding the response mime type for coloring and formatting for the terminal. ([#1168](https://github.com/httpie/httpie/issues/1168))
- Improved handling of responses with incorrect `Content-Type`. ([#1110](https://github.com/httpie/httpie/issues/1110), [#1168](https://github.com/httpie/httpie/issues/1168))
- Installed plugins are now listed in `--debug` output. ([#1165](https://github.com/httpie/httpie/issues/1165))
- Fixed duplicate keys preservation of JSON data. ([#1163](https://github.com/httpie/httpie/issues/1163))
- Added the ability to silence warnings through using `-q` or `--quiet` twice (e.g. `-qq`) ([#1175](https://github.com/httpie/httpie/issues/1175))
- Added installed plugin list to `--debug` output. ([#1165](https://github.com/httpie/httpie/issues/1165))
- Fixed duplicate keys preservation in JSON data. ([#1163](https://github.com/httpie/httpie/issues/1163))
## [2.5.0](https://github.com/httpie/httpie/compare/2.4.0...2.5.0) (2021-09-06)

View File

@ -63,13 +63,13 @@ Do not edit here, but in docs/installation/.
Please make sure you have Python 3.6 or newer (`python --version`).
```bash
# Install
# Install httpie
$ python -m pip install --upgrade pip wheel
$ python -m pip install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ python -m pip install --upgrade pip wheel
$ python -m pip install --upgrade httpie
```
@ -78,61 +78,47 @@ $ python -m pip install --upgrade httpie
#### Homebrew
To install [Homebrew](https://brew.sh/) follow [installation instructions](https://docs.brew.sh/Installation).
To install [Homebrew](https://brew.sh/), see [its installation](https://docs.brew.sh/Installation).
```bash
# Install
# Install httpie
$ brew update
$ brew install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ brew update
$ brew upgrade httpie
```
#### MacPorts
To install [MacPorts](https://www.macports.org/) follow [installation instructions](https://www.macports.org/install.php).
To install [MacPorts](https://www.macports.org/), see [its installation](https://www.macports.org/install.php).
```bash
# Install
# Install httpie
$ port selfupdate
$ port install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ port selfupdate
$ port upgrade httpie
```
#### Snapcraft (macOS)
To install [Snapcraft](https://snapcraft.io/) follow [installation instructions](https://snapcraft.io/docs/installing-snapd).
```bash
# Install
$ snap install httpie
```
```bash
# Upgrade
$ snap refresh httpie
```
#### Spack (macOS)
To install [Spack](https://spack.readthedocs.io/en/latest/index.html) follow [installation instructions](https://spack.readthedocs.io/en/latest/getting_started.html#installation).
To install [Spack](https://spack.readthedocs.io/en/latest/index.html), see [its installation](https://spack.readthedocs.io/en/latest/getting_started.html#installation).
```bash
# Install
# Install httpie
$ spack install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ spack install httpie
```
@ -140,15 +126,15 @@ $ spack install httpie
#### Chocolatey
To install [Chocolatey](https://chocolatey.org/) follow [installation instructions](https://chocolatey.org/install).
To install [Chocolatey](https://chocolatey.org/), see [its installation](https://chocolatey.org/install).
```bash
# Install
# Install httpie
$ choco install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ choco upgrade httpie
```
@ -156,30 +142,30 @@ $ choco upgrade httpie
#### Snapcraft (Linux)
To install [Snapcraft](https://snapcraft.io/) follow [installation instructions](https://snapcraft.io/docs/installing-snapd).
To install [Snapcraft](https://snapcraft.io/), see [its installation](https://snapcraft.io/docs/installing-snapd).
```bash
# Install
# Install httpie
$ snap install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ snap refresh httpie
```
#### Linuxbrew
To install [Linuxbrew](https://docs.brew.sh/Homebrew-on-Linux) follow [installation instructions](https://docs.brew.sh/Homebrew-on-Linux#install).
To install [Linuxbrew](https://docs.brew.sh/Homebrew-on-Linux), see [its installation](https://docs.brew.sh/Homebrew-on-Linux#install).
```bash
# Install
# Install httpie
$ brew update
$ brew install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ brew update
$ brew upgrade httpie
```
@ -189,13 +175,13 @@ $ brew upgrade httpie
Also works for other Debian-derived distributions like MX Linux, Linux Mint, deepin, Pop!_OS, KDE neon, Zorin OS, elementary OS, Kubuntu, Devuan, Linux Lite, Peppermint OS, Lubuntu, antiX, Xubuntu, etc.
```bash
# Install
# Install httpie
$ apt update
$ apt install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ apt update
$ apt upgrade httpie
```
@ -203,13 +189,13 @@ $ apt upgrade httpie
#### Fedora
```bash
# Install
# Install httpie
$ dnf update
$ dnf install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ dnf update
$ dnf upgrade httpie
```
@ -219,14 +205,14 @@ $ dnf upgrade httpie
Also works for other RHEL-derived distributions like ClearOS, Oracle Linux, etc.
```bash
# Install
# Install httpie
$ yum update
$ yum install epel-release
$ yum install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ yum update
$ yum upgrade httpie
```
@ -234,13 +220,13 @@ $ yum upgrade httpie
#### Alpine Linux
```bash
# Install
# Install httpie
$ apk update
$ apk add httpie
```
```bash
# Upgrade
# Upgrade httpie
$ apk update
$ apk add --upgrade httpie
```
@ -248,13 +234,13 @@ $ apk add --upgrade httpie
#### Gentoo
```bash
# Install
# Install httpie
$ emerge --sync
$ emerge httpie
```
```bash
# Upgrade
# Upgrade httpie
$ emerge --sync
$ emerge --update httpie
```
@ -264,40 +250,40 @@ $ emerge --update httpie
Also works for other Arch-derived distributions like ArcoLinux, EndeavourOS, Artix Linux, etc.
```bash
# Install
# Install httpie
$ pacman -Sy httpie
```
```bash
# Upgrade
# Upgrade httpie
$ pacman -Syu httpie
```
#### Void Linux
```bash
# Install
# Install httpie
$ xbps-install -Su
$ xbps-install -S httpie
```
```bash
# Upgrade
# Upgrade httpie
$ xbps-install -Su
$ xbps-install -Su httpie
```
#### Spack (Linux)
To install [Spack](https://spack.readthedocs.io/en/latest/index.html) follow [installation instructions](https://spack.readthedocs.io/en/latest/getting_started.html#installation).
To install [Spack](https://spack.readthedocs.io/en/latest/index.html), see [its installation](https://spack.readthedocs.io/en/latest/getting_started.html#installation).
```bash
# Install
# Install httpie
$ spack install httpie
```
```bash
# Upgrade
# Upgrade httpie
$ spack install httpie
```
@ -306,12 +292,12 @@ $ spack install httpie
#### FreshPorts
```bash
# Install
# Install httpie
$ pkg install www/py-httpie
```
```bash
# Upgrade
# Upgrade httpie
$ pkg upgrade www/py-httpie
```
@ -348,7 +334,7 @@ Verify that now you have the [current development version identifier](https://gi
```bash
$ http --version
# 2.6.0.dev0
# 2.6.0
```
## Usage
@ -1247,6 +1233,13 @@ This doesnt affect output to a file via `--output` or `--download`.
It takes the same arguments as `--print, -p` but applies to the intermediary requests only.
```bash
# Print the intermediary requests/responses differently than the final one:
$ http -A digest -a foo:bar --all -p Hh -P H pie.dev/digest-auth/auth/foo/bar
```
### Conditional body download
As an optimization, the response body is downloaded from the server only if its part of the output.
This is similar to performing a `HEAD` request, except that it applies to any HTTP method you use.
Lets say that there is an API that returns the whole resource when it is updated, but you are only interested in the response headers to see the status code after an update:
@ -1297,7 +1290,8 @@ The universal method for passing request data is through redirected `stdin`
$ echo -n '{"name": "John"}' | http PATCH pie.dev/patch X-API-Token:123
```
You can also use a Bash *here string*:
```bash
$ http pie.dev/post <<<'{"name": "John"}'
```
@ -1407,16 +1401,32 @@ $ http --chunked pie.dev/post @files/data.xml
HTTPie does several things by default in order to make its terminal output easy to read.
### Colors and formatting
Syntax highlighting is applied to HTTP headers and bodies (where it makes sense).
You can choose your preferred color scheme via the --style option if you dont like the default one.
There are dozens of styles available, here are just a few notable ones:
| Style | Description |
| --------: | ----------------------------------------------------------------------------------------------------------------------------------- |
| `auto` | Follows your terminal ANSI color styles. This is the default style used by HTTPie |
| `default` | Default styles of the underlying Pygments library. Not actually used by default by HTTPie. You can enable it with `--style=default` |
| `monokai` | A popular color scheme. Enable with `--style=monokai` |
| `fruity` | A bold, colorful scheme. Enable with `--style=fruity` |
| … | See `$ http --help` for all the possible `--style` values |
Use one of these options to control output processing:
| Option | Description |
| ----------------: | ------------------------------------------------------------- |
| `--pretty=all` | Apply both colors and formatting. Default for terminal output |
| `--pretty=colors` | Apply colors |
| `--pretty=format` | Apply formatting |
| `--pretty=none` | Disables output processing. Default for redirected output |
HTTPie looks at `Content-Type` to select the right syntax highlighter and formatter for each message body. If that fails (e.g., the server provides the wrong type), or you prefer a different treatment, you can manually overwrite the mime type for a response with `--response-mime`:
Formatting has the following effects:
```bash
- JSON data is indented, sorted by keys, and unicode escapes are converted
$ http --response-mime=text/yaml pie.dev/get
```
Formatting has the following effects:
@ -1436,6 +1446,12 @@ Use one of these options to control output processing:
| Option | Default value | Shortcuts |
| ---------------: | :-----------: | ------------------------ |
| `headers.sort` | `true` | `--sorted`, `--unsorted` |
| `json.format` | `true` | N/A |
| `json.indent` | `4` | N/A |
| `json.sort_keys` | `true` | `--sorted`, `--unsorted` |
| `xml.format` | `true` | N/A |
| `xml.indent` | `2` | N/A |
For example, this is how you would disable the default header and JSON key
sorting, and specify a custom JSON indent size:
@ -1472,29 +1488,6 @@ sorting-related format options (currently it means JSON keys and headers):
```bash
$ http octodex.github.com/images/original.jpg | convert - -resize 25% - | http example.org/Octocats
```
### Redirected output
HTTPie uses a different set of defaults for redirected output than for [terminal output](#terminal-output).
The differences being:
- Formatting and colors arent applied (unless `--pretty` is specified).
- Only the response body is printed (unless one of the [output options](#output-options) is set).
- Also, binary data isnt suppressed.
The reason is to make piping HTTPies output to another programs and downloading files work with no extra flags.
Most of the time, only the raw response body is of an interest when the output is redirected.
Download a file:
```bash
$ http pie.dev/image/png > image.png
```
Download an image of an [Octocat](https://octodex.github.com/images/original.jpg), resize it using [ImageMagick](https://imagemagick.org/), and upload it elsewhere:
```bash
$ http octodex.github.com/images/original.jpg | convert - -resize 25% - | http example.org/Octocats
```
Force colorizing and formatting, and show both the request and the response in `less` pager:
@ -1557,20 +1550,13 @@ Content-Type: application/octet-stream
Downloading 251.30 kB to "httpie-master.tar.gz"
Done. 251.30 kB in 2.73862s (91.76 kB/s)
HTTP/1.1 200 OK
```
Content-Type: application/x-gzip
### Downloaded filename
Done. 251.30 kB in 2.73862s (91.76 kB/s)
```
### Downloaded filename
There are three mutually exclusive ways through which HTTPie determines
the output filename (with decreasing priority):
There are three mutually exclusive ways through which HTTPie determines
the output filename (with decreasing priority):
1. You can explicitly provide it via `--output, -o`. The file gets overwritten if it already exists (or appended to with `--continue, -c`).
2. The server may specify the filename in the optional `Content-Disposition` response header. Any leading dots are stripped from a server-provided filename.
3. The last resort HTTPie uses is to generate the filename from a combination of the request URL and the response `Content-Type`. The initial URL is always used as the basis for the generated filename — even if there has been one or more redirects.

View File

@ -0,0 +1,3 @@
Here we maintain a database of contributors, from which we generate credits on release blog posts and social medias.
For the HTTPie blog see: <https://httpie.io/blog>.

280
docs/contributors/fetch.py Normal file
View File

@ -0,0 +1,280 @@
"""
Generate the contributors database.
FIXME: replace `requests` calls with the HTTPie API, when available.
"""
import json
import os
import re
import sys
from copy import deepcopy
from datetime import datetime
from pathlib import Path
from subprocess import check_output
from time import sleep
from typing import Any, Dict, Optional, Set
import requests
FullNames = Set[str]
GitHubLogins = Set[str]
Person = Dict[str, str]
People = Dict[str, Person]
UserInfo = Dict[str, Any]
CO_AUTHORS = re.compile(r'Co-authored-by: ([^<]+) <').finditer
API_URL = 'https://api.github.com'
REPO = OWNER = 'httpie'
REPO_URL = f'{API_URL}/repos/{REPO}/{OWNER}'
HERE = Path(__file__).parent
DB_FILE = HERE / 'people.json'
DEFAULT_PERSON: Person = {'committed': [], 'reported': [], 'github': '', 'twitter': ''}
SKIPPED_LABELS = {'invalid'}
GITHUB_TOKEN = os.getenv('GITHUB_TOKEN')
assert GITHUB_TOKEN, 'GITHUB_TOKEN envar is missing'
class FinishedForNow(Exception):
"""Raised when remaining GitHub rate limit is zero."""
def main(previous_release: str, current_release: str) -> int:
since = release_date(previous_release)
until = release_date(current_release)
contributors = load_awesome_people()
try:
committers = find_committers(since, until)
reporters = find_reporters(since, until)
except Exception as exc:
# We want to save what we fetched so far. So pass.
print(' !! ', exc)
try:
merge_all_the_people(current_release, contributors, committers, reporters)
fetch_missing_users_details(contributors)
except FinishedForNow:
# We want to save what we fetched so far. So pass.
print(' !! Committers:', committers)
print(' !! Reporters:', reporters)
exit_status = 1
else:
exit_status = 0
save_awesome_people(contributors)
return exit_status
def find_committers(since: str, until: str) -> FullNames:
url = f'{REPO_URL}/commits'
page = 1
per_page = 100
params = {
'since': since,
'until': until,
'per_page': per_page,
}
committers: FullNames = set()
while 'there are commits':
params['page'] = page
data = fetch(url, params=params)
for item in data:
commit = item['commit']
committers.add(commit['author']['name'])
debug(' >>> Commit', item['html_url'])
for co_author in CO_AUTHORS(commit['message']):
name = co_author.group(1)
committers.add(name)
if len(data) < per_page:
break
page += 1
return committers
def find_reporters(since: str, until: str) -> GitHubLogins:
url = f'{API_URL}/search/issues'
page = 1
per_page = 100
params = {
'q': f'repo:{REPO}/{OWNER} is:issue closed:{since}..{until}',
'per_page': per_page,
}
reporters: GitHubLogins = set()
while 'there are issues':
params['page'] = page
data = fetch(url, params=params)
for item in data['items']:
# Filter out unwanted labels.
if any(label['name'] in SKIPPED_LABELS for label in item['labels']):
continue
debug(' >>> Issue', item['html_url'])
reporters.add(item['user']['login'])
if len(data['items']) < per_page:
break
page += 1
return reporters
def merge_all_the_people(release: str, contributors: People, committers: FullNames, reporters: GitHubLogins) -> None:
"""
>>> contributors = {'Alice': new_person(github='alice', twitter='alice')}
>>> merge_all_the_people('2.6.0', contributors, {}, {})
>>> contributors
{'Alice': {'committed': [], 'reported': [], 'github': 'alice', 'twitter': 'alice'}}
>>> contributors = {'Bob': new_person(github='bob', twitter='bob')}
>>> merge_all_the_people('2.6.0', contributors, {'Bob'}, {'bob'})
>>> contributors
{'Bob': {'committed': ['2.6.0'], 'reported': ['2.6.0'], 'github': 'bob', 'twitter': 'bob'}}
>>> contributors = {'Charlotte': new_person(github='charlotte', twitter='charlotte', committed=['2.5.0'], reported=['2.5.0'])}
>>> merge_all_the_people('2.6.0', contributors, {'Charlotte'}, {'charlotte'})
>>> contributors
{'Charlotte': {'committed': ['2.5.0', '2.6.0'], 'reported': ['2.5.0', '2.6.0'], 'github': 'charlotte', 'twitter': 'charlotte'}}
"""
# Update known contributors.
for name, details in contributors.items():
if name in committers:
if release not in details['committed']:
details['committed'].append(release)
committers.remove(name)
if details['github'] in reporters:
if release not in details['reported']:
details['reported'].append(release)
reporters.remove(details['github'])
# Add new committers.
for name in committers:
user_info = user(fullname=name)
contributors[name] = new_person(
github=user_info['login'],
twitter=user_info['twitter_username'],
committed=[release],
)
if user_info['login'] in reporters:
contributors[name]['reported'].append(release)
reporters.remove(user_info['login'])
# Add new reporters.
for github_username in reporters:
user_info = user(github_username=github_username)
contributors[user_info['name'] or user_info['login']] = new_person(
github=github_username,
twitter=user_info['twitter_username'],
reported=[release],
)
def release_date(release: str) -> str:
date = check_output(['git', 'log', '-1', '--format=%ai', release], text=True).strip()
return datetime.strptime(date, '%Y-%m-%d %H:%M:%S %z').isoformat()
def load_awesome_people() -> People:
try:
with DB_FILE.open(encoding='utf-8') as fh:
return json.load(fh)
except (FileNotFoundError, ValueError):
return {}
def fetch(url: str, params: Optional[Dict[str, str]] = None) -> UserInfo:
headers = {
'Accept': 'application/vnd.github.v3+json',
'Authentication': f'token {GITHUB_TOKEN}'
}
for retry in range(1, 6):
debug(f'[{retry}/5]', f'{url = }', f'{params = }')
with requests.get(url, params=params, headers=headers) as req:
try:
req.raise_for_status()
except requests.exceptions.HTTPError as exc:
if exc.response.status_code == 403:
# 403 Client Error: rate limit exceeded for url: ...
now = int(datetime.utcnow().timestamp())
xrate_limit_reset = int(exc.response.headers['X-RateLimit-Reset'])
wait = xrate_limit_reset - now
if wait > 20:
raise FinishedForNow()
debug(' !', 'Waiting', wait, 'seconds before another try ...')
sleep(wait)
continue
return req.json()
assert ValueError('Rate limit exceeded')
def new_person(**kwargs: str) -> Person:
data = deepcopy(DEFAULT_PERSON)
data.update(**kwargs)
return data
def user(fullname: Optional[str] = '', github_username: Optional[str] = '') -> UserInfo:
if github_username:
url = f'{API_URL}/users/{github_username}'
return fetch(url)
url = f'{API_URL}/search/users'
for query in (f'fullname:{fullname}', f'user:{fullname}'):
params = {
'q': f'repo:{REPO}/{OWNER} {query}',
'per_page': 1,
}
user_info = fetch(url, params=params)
if user_info['items']:
user_url = user_info['items'][0]['url']
return fetch(user_url)
def fetch_missing_users_details(people: People) -> None:
for name, details in people.items():
if details['github'] and details['twitter']:
continue
user_info = user(github_username=details['github'], fullname=name)
if not details['github']:
details['github'] = user_info['login']
if not details['twitter']:
details['twitter'] = user_info['twitter_username']
def save_awesome_people(people: People) -> None:
with DB_FILE.open(mode='w', encoding='utf-8') as fh:
json.dump(people, fh, indent=4, sort_keys=True)
def debug(*args: Any) -> None:
if os.getenv('DEBUG') == '1':
print(*args)
if __name__ == '__main__':
ret = 1
try:
ret = main(*sys.argv[1:])
except TypeError:
ret = 2
print(f'''
Fetch contributors to a release.
Usage:
python {sys.argv[0]} {sys.argv[0]} <RELEASE N-1> <RELEASE N>
Example:
python {sys.argv[0]} 2.4.0 2.5.0
Define the DEBUG=1 environment variable to enable verbose output.
''')
except KeyboardInterrupt:
ret = 255
sys.exit(ret)

View File

@ -0,0 +1,41 @@
"""
Generate snippets to copy-paste.
"""
import sys
from jinja2 import Template
from fetch import HERE, load_awesome_people
TPL_FILE = HERE / 'snippet.jinja2'
HTTPIE_TEAM = {'jakubroztocil', 'BoboTiG', 'claudiatd'}
def generate_snippets(release: str) -> str:
people = load_awesome_people()
contributors = {
name: details
for name, details in people.items()
if details['github'] not in HTTPIE_TEAM
and (release in details['committed'] or release in details['reported'])
}
template = Template(source=TPL_FILE.read_text(encoding='utf-8'))
output = template.render(contributors=contributors, release=release)
print(output)
return 0
if __name__ == '__main__':
ret = 1
try:
ret = generate_snippets(sys.argv[1])
except (IndexError, TypeError):
ret = 2
print(f'''
Generate snippets for contributors to a release.
Usage:
python {sys.argv[0]} {sys.argv[0]} <RELEASE>
''')
sys.exit(ret)

View File

@ -0,0 +1,240 @@
{
"Almad": {
"committed": [
"2.5.0"
],
"github": "Almad",
"reported": [],
"twitter": "almadcz"
},
"Anton Emelyanov": {
"committed": [
"2.5.0"
],
"github": "king-menin",
"reported": [],
"twitter": null
},
"D8ger": {
"committed": [],
"github": "caofanCPU",
"reported": [
"2.5.0"
],
"twitter": null
},
"Dawid Ferenczy Rogo\u017ean": {
"committed": [],
"github": "ferenczy",
"reported": [
"2.5.0"
],
"twitter": "DawidFerenczy"
},
"Elena Lape": {
"committed": [
"2.5.0"
],
"github": "elenalape",
"reported": [],
"twitter": "elena_lape"
},
"F\u00fash\u0113ng": {
"committed": [],
"github": "lienide",
"reported": [
"2.5.0"
],
"twitter": null
},
"Giampaolo Rodola": {
"committed": [],
"github": "giampaolo",
"reported": [
"2.5.0"
],
"twitter": null
},
"Hugh Williams": {
"committed": [],
"github": "hughpv",
"reported": [
"2.5.0"
],
"twitter": null
},
"Ilya Sukhanov": {
"committed": [
"2.5.0"
],
"github": "IlyaSukhanov",
"reported": [
"2.5.0"
],
"twitter": null
},
"Jakub Roztocil": {
"committed": [
"2.5.0"
],
"github": "jakubroztocil",
"reported": [
"2.5.0"
],
"twitter": "jakubroztocil"
},
"Jan Verbeek": {
"committed": [
"2.5.0"
],
"github": "blyxxyz",
"reported": [],
"twitter": null
},
"Jannik Vieten": {
"committed": [
"2.5.0"
],
"github": "exploide",
"reported": [],
"twitter": null
},
"Marcel St\u00f6r": {
"committed": [
"2.5.0"
],
"github": "marcelstoer",
"reported": [],
"twitter": "frightanic"
},
"Mariano Ruiz": {
"committed": [],
"github": "mrsarm",
"reported": [
"2.5.0"
],
"twitter": "mrsarm82"
},
"Micka\u00ebl Schoentgen": {
"committed": [
"2.5.0"
],
"github": "BoboTiG",
"reported": [
"2.5.0"
],
"twitter": "__tiger222__"
},
"Miro Hron\u010dok": {
"committed": [
"2.5.0"
],
"github": "hroncok",
"reported": [],
"twitter": "hroncok"
},
"Mohamed Daahir": {
"committed": [],
"github": "ducaale",
"reported": [
"2.5.0"
],
"twitter": null
},
"Pavel Alexeev aka Pahan-Hubbitus": {
"committed": [],
"github": "Hubbitus",
"reported": [
"2.5.0"
],
"twitter": null
},
"Samuel Marks": {
"committed": [],
"github": "SamuelMarks",
"reported": [
"2.5.0"
],
"twitter": null
},
"Sullivan SENECHAL": {
"committed": [],
"github": "soullivaneuh",
"reported": [
"2.5.0"
],
"twitter": null
},
"Thomas Klinger": {
"committed": [],
"github": "mosesontheweb",
"reported": [
"2.5.0"
],
"twitter": null
},
"Yannic Schneider": {
"committed": [],
"github": "cynay",
"reported": [
"2.5.0"
],
"twitter": null
},
"a1346054": {
"committed": [
"2.5.0"
],
"github": "a1346054",
"reported": [],
"twitter": null
},
"bl-ue": {
"committed": [
"2.5.0"
],
"github": "FiReBlUe45",
"reported": [],
"twitter": null
},
"henryhu712": {
"committed": [
"2.5.0"
],
"github": "henryhu712",
"reported": [],
"twitter": null
},
"jungle-boogie": {
"committed": [],
"github": "jungle-boogie",
"reported": [
"2.5.0"
],
"twitter": null
},
"nixbytes": {
"committed": [
"2.5.0"
],
"github": "nixbytes",
"reported": [],
"twitter": "linuxbyte3"
},
"qiulang": {
"committed": [],
"github": "qiulang",
"reported": [
"2.5.0"
],
"twitter": null
},
"zwx00": {
"committed": [],
"github": "zwx00",
"reported": [
"2.5.0"
],
"twitter": null
}
}

View File

@ -0,0 +1,13 @@
<!-- Blog post -->
## Community contributions
Wed like to thank these amazing people for their contributions to this release: {% for name, details in contributors.items() -%}
[{{ name }}](https://github.com/{{ details.github }}){{ '' if loop.last else ', ' }}
{%- endfor %}.
<!-- Twitter -->
Wed like to thank these amazing people for their contributions to HTTPie {{ release }}: {% for name, details in contributors.items() if details.twitter -%}
@{{ details.twitter }}{{ '' if loop.last else ', ' }}
{%- endfor %} 🥧

View File

@ -19,16 +19,16 @@ Do not edit here, but in docs/installation/.
{% endif %}
{% if tool.links.setup %}
To install [{{ tool.name }}]({{ tool.links.homepage }}) follow [installation instructions]({{ tool.links.setup }}).
To install [{{ tool.name }}]({{ tool.links.homepage }}), see [its installation]({{ tool.links.setup }}).
{% endif %}
```bash
# Install
# Install httpie
$ {{ tool.commands.install|join('\n$ ') }}
```
```bash
# Upgrade
# Upgrade httpie
$ {{ tool.commands.upgrade|join('\n$ ') }}
```
{% endfor %}

View File

@ -14,7 +14,6 @@ docs-structure:
macOS:
- brew-mac
- port
- snap-mac
- spack-mac
Windows:
- chocolatey
@ -202,19 +201,6 @@ tools:
upgrade:
- snap refresh httpie
snap-mac:
title: Snapcraft (macOS)
name: Snapcraft
links:
homepage: https://snapcraft.io/
setup: https://snapcraft.io/docs/installing-snapd
package: https://snapcraft.io/httpie
commands:
install:
- snap install httpie
upgrade:
- snap refresh httpie
spack-linux:
title: Spack (Linux)
name: Spack

View File

@ -23,7 +23,8 @@ That is done quite easily by manually triggering the [release workflow](https://
## Then, company-specific tasks
- Update the HTTPie version bundled into termible ([example](https://github.com/httpie/termible/pull/1)).
- Blank the `master_and_released_docs_differ_after` value in [config.json](https://github.com/httpie/httpie/blob/master/docs/config.json).
- Update the HTTPie version bundled into [Termible](https://termible.io/) ([example](https://github.com/httpie/termible/pull/1)).
## Finally, spread dowstream

View File

@ -1,6 +1,2 @@
$ErrorActionPreference = 'Stop';
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$nuspecPath = "$(Join-Path (Split-Path -parent $toolsDir) ($env:ChocolateyPackageName + ".nuspec"))"
[XML]$nuspec = Get-Content $nuspecPath
$pipVersion = $nuspec.package.metadata.version
py -m pip install "$($env:ChocolateyPackageName)==$($pipVersion)" --disable-pip-version-check
py -m pip install $env:ChocolateyPackageName==$env:ChocolateyPackageVersion --disable-pip-version-check

View File

@ -3,6 +3,6 @@ HTTPie: command-line HTTP client for the API era.
"""
__version__ = '2.6.0.dev0'
__version__ = '2.6.0'
__author__ = 'Jakub Roztocil'
__licence__ = 'BSD'

View File

@ -497,12 +497,14 @@ output_options.add_argument(
output_options.add_argument(
'--quiet', '-q',
action='store_true',
default=False,
action='count',
default=0,
help='''
Do not print to stdout or stderr.
Do not print to stdout or stderr, except for errors and warnings when provided once.
Provide twice to suppress warnings as well.
stdout is still redirected if --output is specified.
Flag doesn't affect behaviour of download beyond not printing to terminal.
'''
)

View File

@ -185,7 +185,7 @@ def program(args: argparse.Namespace, env: Environment) -> ExitStatus:
final_response = message
if args.check_status or downloader:
exit_status = http_status_to_exit_status(http_status=message.status_code, follow=args.follow)
if exit_status != ExitStatus.SUCCESS and (not env.stdout_isatty or args.quiet):
if exit_status != ExitStatus.SUCCESS and (not env.stdout_isatty or args.quiet == 1):
env.log_error(f'HTTP {message.raw.status} {message.raw.reason}', level='warning')
write_message(requests_message=message, env=env, args=args, with_headers=with_headers,
with_body=do_write_body)

View File

@ -20,7 +20,7 @@ class EnhancedJsonLexer(RegexLexer):
tokens = {
'root': [
# Eventual non-JSON data prefix followed by actual JSON body.
# FIX: data prefix + number (integer or float) are not correctly handled.
# FIX: data prefix + number (integer or float) is not correctly handled.
(
fr'({PREFIX_REGEX})' + r'((?:[{\["]|true|false|null).+)',
bygroups(PREFIX_TOKEN, using(JsonLexer))

View File

@ -39,15 +39,16 @@ def test_output_option(tmp_path, httpbin, stdout_isatty):
class TestQuietFlag:
QUIET_SCENARIOS = [('--quiet',), ('-q',), ('--quiet', '--quiet'), ('-qq',)]
@pytest.mark.parametrize('argument_name', ['--quiet', '-q'])
def test_quiet(self, httpbin, argument_name):
@pytest.mark.parametrize('quiet_flags', QUIET_SCENARIOS)
def test_quiet(self, httpbin, quiet_flags):
env = MockEnvironment(
stdin_isatty=True,
stdout_isatty=True,
devnull=io.BytesIO()
)
r = http(argument_name, 'GET', httpbin.url + '/get', env=env)
r = http(*quiet_flags, 'GET', httpbin.url + '/get', env=env)
assert env.stdout is env.devnull
assert env.stderr is env.devnull
assert HTTP_OK in r.devnull
@ -69,9 +70,25 @@ class TestQuietFlag:
)
assert 'http: warning: HTTP 500' in r.stderr
def test_quiet_quiet_with_check_status_non_zero(self, httpbin):
r = http(
'--quiet', '--quiet', '--check-status', httpbin + '/status/500',
tolerate_error_exit_status=True,
)
assert not r.stderr
def test_quiet_quiet_with_check_status_non_zero_pipe(self, httpbin):
r = http(
'--quiet', '--quiet', '--check-status', httpbin + '/status/500',
tolerate_error_exit_status=True,
env=MockEnvironment(stdout_isatty=False)
)
assert 'http: warning: HTTP 500' in r.stderr
@pytest.mark.parametrize('quiet_flags', QUIET_SCENARIOS)
@mock.patch('httpie.cli.argtypes.AuthCredentials._getpass',
new=lambda self, prompt: 'password')
def test_quiet_with_password_prompt(self, httpbin):
def test_quiet_with_password_prompt(self, httpbin, quiet_flags):
"""
Tests whether httpie still prompts for a password when request
requires authentication and only username is provided
@ -83,7 +100,7 @@ class TestQuietFlag:
devnull=io.BytesIO()
)
r = http(
'--quiet', '--auth', 'user', 'GET',
*quiet_flags, '--auth', 'user', 'GET',
httpbin.url + '/basic-auth/user/password',
env=env
)
@ -93,17 +110,19 @@ class TestQuietFlag:
assert r == ''
assert r.stderr == ''
@pytest.mark.parametrize('argument_name', ['-h', '-b', '-v', '-p=hH'])
def test_quiet_with_explicit_output_options(self, httpbin, argument_name):
@pytest.mark.parametrize('quiet_flags', QUIET_SCENARIOS)
@pytest.mark.parametrize('output_options', ['-h', '-b', '-v', '-p=hH'])
def test_quiet_with_explicit_output_options(self, httpbin, quiet_flags, output_options):
env = MockEnvironment(stdin_isatty=True, stdout_isatty=True)
r = http('--quiet', argument_name, httpbin.url + '/get', env=env)
r = http(*quiet_flags, output_options, httpbin.url + '/get', env=env)
assert env.stdout is env.devnull
assert env.stderr is env.devnull
assert r == ''
assert r.stderr == ''
@pytest.mark.parametrize('quiet_flags', QUIET_SCENARIOS)
@pytest.mark.parametrize('with_download', [True, False])
def test_quiet_with_output_redirection(self, tmp_path, httpbin, with_download):
def test_quiet_with_output_redirection(self, tmp_path, httpbin, quiet_flags, with_download):
url = httpbin + '/robots.txt'
output_path = Path('output.txt')
env = MockEnvironment()
@ -114,7 +133,7 @@ class TestQuietFlag:
try:
assert os.listdir('.') == []
r = http(
'--quiet',
*quiet_flags,
'--output', str(output_path),
*extra_args,
url,
@ -142,7 +161,7 @@ class TestVerboseFlag:
def test_verbose_raw(self, httpbin):
r = http('--verbose', '--raw', 'foo bar',
'POST', httpbin.url + '/post',)
'POST', httpbin.url + '/post')
assert HTTP_OK in r
assert 'foo bar' in r