Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8754bdded9 |
@@ -1,2 +0,0 @@
|
||||
[run]
|
||||
omit = requests/packages/*
|
||||
@@ -1,5 +0,0 @@
|
||||
# You can configure git to automatically use this file with the following config:
|
||||
# git config --global blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
|
||||
# Add automatic code formatting to Requests
|
||||
2a6f290bc09324406708a4d404a88a45d848ddf9
|
||||
81
.github/AI_POLICY.md
vendored
@@ -1,81 +0,0 @@
|
||||
# Generative AI / LLM Policy
|
||||
|
||||
We appreciate that we can't realistically police how you author your pull requests, which includes whether you employ large-language model (LLM)-based development tools.
|
||||
So, we don't.
|
||||
|
||||
However, due to both legal and human reasons, we have to establish boundaries.
|
||||
|
||||
> [!CAUTION]
|
||||
> **TL;DR:**
|
||||
> - We take the responsibility for this project very seriously and we expect you to take your responsibility for your contributions seriously, too.
|
||||
> This used to be a given, but it changed now that a pull request is just one prompt away.
|
||||
>
|
||||
> - Every contribution has to be backed by a human who unequivocally owns the copyright for all changes.
|
||||
> No LLM bots in `Co-authored-by:`s.
|
||||
>
|
||||
> - DoS-by-slop leads to a permanent ban.
|
||||
>
|
||||
> - Absolutely **no** unsupervised agentic tools like OpenClaw.
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> By submitting a pull request, you certify that:
|
||||
>
|
||||
> - You are the author of the contribution or have the legal right to submit it.
|
||||
> - You either hold the copyright to the changes or have explicit legal authorization to contribute them under this project's license.
|
||||
> - You understand the code.
|
||||
> - You accept full responsibility for it.
|
||||
|
||||
|
||||
## Legal
|
||||
|
||||
There is ongoing legal uncertainty regarding the copyright status of LLM-generated works and their provenance.
|
||||
Since we do not have a formal [Contributor License Agreement](https://en.wikipedia.org/wiki/Contributor_license_agreement) (CLA), you retain your copyright to your changes to this project.
|
||||
|
||||
Therefore, allowing contributions by LLMs has unpredictable consequences for the copyright status of this project – even when leaving aside possible copyright violations due to plagiarism.
|
||||
|
||||
|
||||
## Human
|
||||
|
||||
As the makers of software that is used by millions of people worldwide and with a reputation for high-quality maintenance, we take our responsibility to our users very seriously.
|
||||
No matter what LLM vendors or boosters on LinkedIn tell you, we have to manually review every change before merging, because it's **our responsibility** to keep the project stable.
|
||||
|
||||
Please understand that by opening low-quality pull requests you're not helping anyone.
|
||||
Worse, you're [poisoning the open source ecosystem](https://lwn.net/Articles/1058266/) that was precarious even before the arrival of LLM tools.
|
||||
Having to wade through plausible-looking-but-low-quality pull requests and trying to determine which ones are legit is extremely demoralizing and has already burned out many good maintainers.
|
||||
|
||||
Put bluntly, we have no time or interest to become part of your vibe coding loop where you drop LLM slop at our door, we spend time and energy to review it, and you just feed it back into the LLM for another iteration.
|
||||
|
||||
This dynamic is especially pernicious because it poisons the well for mentoring new contributors which we are committed to.
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
In practice, this means:
|
||||
|
||||
- Pull requests that have an LLM product listed as co-author can't be merged and will be closed without further discussion.
|
||||
We cannot risk the copyright status of this project.
|
||||
|
||||
If you used LLM tools during development, you may still submit – but you must remove any LLM co-author tags and take full ownership of every line.
|
||||
|
||||
- By submitting a pull request, **you** take full **technical and legal** responsibility for the contents of the pull request and promise that **you** hold the copyright for the changes submitted.
|
||||
|
||||
"An LLM wrote it" is **not** an acceptable response to questions or critique.
|
||||
**If you cannot explain and defend the changes you submit, do not submit them** and open a high-quality bug report/feature request instead.
|
||||
|
||||
- Accounts that exercise bot-like behavior – like automated mass pull requests – will be permanently banned, whether they belong to a human or not.
|
||||
|
||||
- Do **not** post LLM-generated review comments – we can prompt LLMs ourselves should we desire their wisdom.
|
||||
Do **not** post summaries unless you've fact-checked them and take responsibility for 100% of their content.
|
||||
Remember that *all* LLM output *looks* **plausible**.
|
||||
When using these tools, it's **your** responsibility to ensure that it's also **correct** and has a reasonable signal-to-noise ratio.
|
||||
|
||||
---
|
||||
|
||||
# Credits, Attribution
|
||||
|
||||
The original version of this can be found in [pyca/service-identity][] and is
|
||||
used with permission from Hynek Schlawack.
|
||||
|
||||
[pyca/service-identity]:
|
||||
https://github.com/pyca/service-identity/blob/fa91bf55cfda64145aa3d202cc84059befb98af4/.github/AI_POLICY.md
|
||||
8
.github/CODEOWNERS
vendored
@@ -1,8 +0,0 @@
|
||||
# Restrict all files related to deploying to
|
||||
# require lead maintainer approval.
|
||||
|
||||
.github/workflows/ @nateprewitt @sigmavirus24
|
||||
.github/CODEOWNERS @nateprewitt @sigmavirus24
|
||||
src/requests/__version__.py @nateprewitt @sigmavirus24
|
||||
HISTORY.md @nateprewitt @sigmavirus24
|
||||
pyproject.toml @nateprewitt @sigmavirus24
|
||||
6
.github/CODE_OF_CONDUCT.md
vendored
@@ -1,6 +0,0 @@
|
||||
# Treat each other well
|
||||
|
||||
Everyone participating in the _requests_ project, and in particular in the issue tracker,
|
||||
pull requests, and social media activity, is expected to treat other people with respect
|
||||
and more generally to follow the guidelines articulated in the
|
||||
[Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/).
|
||||
66
.github/CONTRIBUTING.md
vendored
@@ -1,66 +0,0 @@
|
||||
# Contribution Guidelines
|
||||
|
||||
Before opening any issues or proposing any pull requests, please read
|
||||
our [Contributor's Guide](https://requests.readthedocs.io/en/latest/dev/contributing/)
|
||||
as well as our [AI Policy](/.github/AI_POLICY.md).
|
||||
|
||||
To get the greatest chance of helpful responses, please also observe the
|
||||
following additional notes.
|
||||
|
||||
## Questions
|
||||
|
||||
The GitHub issue tracker is for *bug reports* and *feature requests*. Please do
|
||||
not use it to ask questions about how to use Requests. These questions should
|
||||
instead be directed to [Stack Overflow](https://stackoverflow.com/). Make sure
|
||||
that your question is tagged with the `python-requests` tag when asking it on
|
||||
Stack Overflow, to ensure that it is answered promptly and accurately.
|
||||
|
||||
## Good Bug Reports
|
||||
|
||||
Please be aware of the following things when filing bug reports:
|
||||
|
||||
1. Avoid raising duplicate issues. *Please* use the GitHub issue search feature
|
||||
to check whether your bug report or feature request has been mentioned in
|
||||
the past. Duplicate bug reports and feature requests are a huge maintenance
|
||||
burden on the limited resources of the project. If it is clear from your
|
||||
report that you would have struggled to find the original, that's ok, but
|
||||
if searching for a selection of words in your issue title would have found
|
||||
the duplicate then the issue will likely be closed extremely abruptly.
|
||||
2. When filing bug reports about exceptions or tracebacks, please include the
|
||||
*complete* traceback. Partial tracebacks, or just the exception text, are
|
||||
not helpful. Issues that do not contain complete tracebacks may be closed
|
||||
without warning.
|
||||
3. Make sure you provide a suitable amount of information to work with. This
|
||||
means you should provide:
|
||||
|
||||
- Guidance on **how to reproduce the issue**. Ideally, this should be a
|
||||
*small* code sample that can be run immediately by the maintainers.
|
||||
Failing that, let us know what you're doing, how often it happens, what
|
||||
environment you're using, etc. Be thorough: it prevents us needing to ask
|
||||
further questions.
|
||||
- Tell us **what you expected to happen**. When we run your example code,
|
||||
what are we expecting to happen? What does "success" look like for your
|
||||
code?
|
||||
- Tell us **what actually happens**. It's not helpful for you to say "it
|
||||
doesn't work" or "it fails". Tell us *how* it fails: do you get an
|
||||
exception? A hang? A non-200 status code? How was the actual result
|
||||
different from your expected result?
|
||||
- Tell us **what version of Requests you're using**, and
|
||||
**how you installed it**. Different versions of Requests behave
|
||||
differently and have different bugs, and some distributors of Requests
|
||||
ship patches on top of the code we supply.
|
||||
|
||||
If you do not provide all of these things, it will take us much longer to
|
||||
fix your problem. If we ask you to clarify these and you never respond, we
|
||||
will close your issue without fixing it.
|
||||
|
||||
## Good Pull Requests
|
||||
|
||||
1. Avoid raising duplicate pull requests. *Please* use the GitHub pull request
|
||||
search feature to check whether someone else has already filed a pull
|
||||
request.
|
||||
2. When filing a pull request, always include tests that fail without your
|
||||
changes thus preventing the fix from regressing.
|
||||
3. Always run the test suite locally
|
||||
4. Write commit messages that explain _why_ a change was made beyond "Fixes
|
||||
#NNNN". Why is this the right fix? What other options were ruled out? etc.
|
||||
1
.github/FUNDING.yml
vendored
@@ -1 +0,0 @@
|
||||
custom: ['https://www.python.org/psf/sponsorship/']
|
||||
28
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,28 +0,0 @@
|
||||
Summary.
|
||||
|
||||
## Expected Result
|
||||
|
||||
What you expected.
|
||||
|
||||
## Actual Result
|
||||
|
||||
What happened instead.
|
||||
|
||||
## Reproduction Steps
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
```
|
||||
|
||||
## System Information
|
||||
|
||||
$ python -m requests.help
|
||||
|
||||
```
|
||||
<paste here>
|
||||
```
|
||||
|
||||
This command is only available on Requests v2.16.4 and greater. Otherwise,
|
||||
please provide some basic information about your system (Python version,
|
||||
operating system, &c).
|
||||
36
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
@@ -1,36 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
<!-- Summary. -->
|
||||
|
||||
## Expected Result
|
||||
|
||||
<!-- What you expected. -->
|
||||
|
||||
## Actual Result
|
||||
|
||||
<!-- What happened instead. -->
|
||||
|
||||
## Reproduction Steps
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
```
|
||||
|
||||
## System Information
|
||||
|
||||
$ python -m requests.help
|
||||
|
||||
```json
|
||||
{
|
||||
"paste": "here"
|
||||
}
|
||||
```
|
||||
|
||||
<!-- This command is only available on Requests v2.16.4 and greater. Otherwise,
|
||||
please provide some basic information about your system (Python version,
|
||||
operating system, &c). -->
|
||||
10
.github/ISSUE_TEMPLATE/Custom.md
vendored
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: Request for Help
|
||||
about: Guidance on using Requests.
|
||||
labels:
|
||||
- "Question/Not a bug"
|
||||
- "actions/autoclose-qa"
|
||||
|
||||
---
|
||||
|
||||
Please refer to our [Stack Overflow tag](https://stackoverflow.com/questions/tagged/python-requests) for guidance.
|
||||
10
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
labels:
|
||||
- "Feature Request"
|
||||
- "actions/autoclose-feat"
|
||||
|
||||
---
|
||||
|
||||
Requests is not accepting feature requests at this time.
|
||||
84
.github/SECURITY.md
vendored
@@ -1,84 +0,0 @@
|
||||
# Vulnerability Disclosure
|
||||
|
||||
If you think you have found a potential security vulnerability in
|
||||
requests, please open a [draft Security Advisory](https://github.com/psf/requests/security/advisories/new)
|
||||
via GitHub. We will coordinate verification and next steps through
|
||||
that secure medium.
|
||||
|
||||
If English is not your first language, please try to describe the
|
||||
problem and its impact to the best of your ability. For greater detail,
|
||||
please use your native language and we will try our best to translate it
|
||||
using online services.
|
||||
|
||||
Please also include the code you used to find the problem and the
|
||||
shortest amount of code necessary to reproduce it.
|
||||
|
||||
Please do not disclose this to anyone else. We will retrieve a CVE
|
||||
identifier if necessary and give you full credit under whatever name or
|
||||
alias you provide. We will only request an identifier when we have a fix
|
||||
and can publish it in a release.
|
||||
|
||||
We will respect your privacy and will only publicize your involvement if
|
||||
you grant us permission.
|
||||
|
||||
## Process
|
||||
|
||||
This following information discusses the process the requests project
|
||||
follows in response to vulnerability disclosures. If you are disclosing
|
||||
a vulnerability, this section of the documentation lets you know how we
|
||||
will respond to your disclosure.
|
||||
|
||||
### Timeline
|
||||
|
||||
When you report an issue, one of the project members will respond to you
|
||||
within two days *at the outside*. In most cases responses will be
|
||||
faster, usually within 12 hours. This initial response will at the very
|
||||
least confirm receipt of the report.
|
||||
|
||||
If we were able to rapidly reproduce the issue, the initial response
|
||||
will also contain confirmation of the issue. If we are not, we will
|
||||
often ask for more information about the reproduction scenario.
|
||||
|
||||
Our goal is to have a fix for any vulnerability released within two
|
||||
weeks of the initial disclosure. This may potentially involve shipping
|
||||
an interim release that simply disables function while a more mature fix
|
||||
can be prepared, but will in the vast majority of cases mean shipping a
|
||||
complete release as soon as possible.
|
||||
|
||||
Throughout the fix process we will keep you up to speed with how the fix
|
||||
is progressing. Once the fix is prepared, we will notify you that we
|
||||
believe we have a fix. Often we will ask you to confirm the fix resolves
|
||||
the problem in your environment, especially if we are not confident of
|
||||
our reproduction scenario.
|
||||
|
||||
At this point, we will prepare for the release. We will obtain a CVE
|
||||
number if one is required, providing you with full credit for the
|
||||
discovery. We will also decide on a planned release date, and let you
|
||||
know when it is. This release date will *always* be on a weekday.
|
||||
|
||||
At this point we will reach out to our major downstream packagers to
|
||||
notify them of an impending security-related patch so they can make
|
||||
arrangements. In addition, these packagers will be provided with the
|
||||
intended patch ahead of time, to ensure that they are able to promptly
|
||||
release their downstream packages. Currently the list of people we
|
||||
actively contact *ahead of a public release* is:
|
||||
|
||||
- Python Maintenance Team, Red Hat (python-maint@redhat.com)
|
||||
- Daniele Tricoli, Debian (@eriol)
|
||||
|
||||
We will notify these individuals at least a week ahead of our planned
|
||||
release date to ensure that they have sufficient time to prepare. If you
|
||||
believe you should be on this list, please let one of the maintainers
|
||||
know at one of the email addresses at the top of this article.
|
||||
|
||||
On release day, we will push the patch to our public repository, along
|
||||
with an updated changelog that describes the issue and credits you. We
|
||||
will then issue a PyPI release containing the patch.
|
||||
|
||||
At this point, we will publicise the release. This will involve mails to
|
||||
mailing lists, Tweets, and all other communication mechanisms available
|
||||
to the core team.
|
||||
|
||||
We will also explicitly mention which commits contain the fix to make it
|
||||
easier for other distributors and users to easily patch their own
|
||||
versions of requests if upgrading is not an option.
|
||||
30
.github/dependabot.yml
vendored
@@ -1,30 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
ignore:
|
||||
# Ignore all patch releases as we can manually
|
||||
# upgrade if we run into a bug and need a fix.
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-patch"]
|
||||
cooldown:
|
||||
default-days: 7
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- "*"
|
||||
- package-ecosystem: "pre-commit"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
ignore:
|
||||
- dependency-name: "*"
|
||||
update-types: ["version-update:semver-patch"]
|
||||
cooldown:
|
||||
default-days: 7
|
||||
groups:
|
||||
pre-commit:
|
||||
patterns:
|
||||
- "*"
|
||||
38
.github/workflows/close-issues.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: 'Autoclose Issues'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
close_qa:
|
||||
permissions:
|
||||
issues: write
|
||||
if: github.event.label.name == 'actions/autoclose-qa'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- env:
|
||||
ISSUE_URL: ${{ github.event.issue.html_url }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh issue close $ISSUE_URL \
|
||||
--comment "As described in the template, we won't be able to answer questions on this issue tracker. Please use [Stack Overflow](https://stackoverflow.com/)" \
|
||||
--reason completed
|
||||
gh issue lock $ISSUE_URL --reason off_topic
|
||||
close_feature_request:
|
||||
permissions:
|
||||
issues: write
|
||||
if: github.event.label.name == 'actions/autoclose-feat'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- env:
|
||||
ISSUE_URL: ${{ github.event.issue.html_url }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh issue close $ISSUE_URL \
|
||||
--comment "As described in the template, Requests is not accepting feature requests" \
|
||||
--reason "not planned"
|
||||
gh issue lock $ISSUE_URL --reason off_topic
|
||||
74
.github/workflows/codeql-analysis.yml
vendored
@@ -1,74 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: '0 23 * * 0'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
permissions:
|
||||
actions: read # for github/codeql-action/init to get workflow details
|
||||
contents: read # for actions/checkout to fetch code
|
||||
security-events: write # for github/codeql-action/autobuild to send a status report
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
persist-credentials: false
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
with:
|
||||
languages: "python"
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
24
.github/workflows/lint.yml
vendored
@@ -1,24 +0,0 @@
|
||||
name: Lint code
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Run pre-commit
|
||||
run: |
|
||||
python -m pip install pre-commit==4.6.0
|
||||
pre-commit run --show-diff-on-failure --color=always --all-files
|
||||
19
.github/workflows/lock-issues.yml
vendored
@@ -1,19 +0,0 @@
|
||||
name: 'Lock Threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
action:
|
||||
if: github.repository_owner == 'psf'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@89ae32b08ed1a541efecbab17912962a5e38981c # v6.0.2
|
||||
with:
|
||||
issue-lock-inactive-days: 90
|
||||
pr-lock-inactive-days: 90
|
||||
95
.github/workflows/publish.yml
vendored
@@ -1,95 +0,0 @@
|
||||
name: Publish to PyPI
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
test-pypi-only:
|
||||
description: "Publish to Test PyPI only"
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build dists"
|
||||
runs-on: "ubuntu-latest"
|
||||
outputs:
|
||||
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
|
||||
|
||||
steps:
|
||||
- name: "Checkout repository"
|
||||
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Setup Python"
|
||||
uses: "actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97" # v7.0.0
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: "Install dependencies"
|
||||
run: python -m pip install build==1.4.0
|
||||
|
||||
- name: "Build dists"
|
||||
run: |
|
||||
SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct) \
|
||||
python -m build
|
||||
|
||||
- name: "Upload dists"
|
||||
uses: "actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f"
|
||||
id: upload-artifact
|
||||
with:
|
||||
name: "dist"
|
||||
path: "dist/"
|
||||
if-no-files-found: error
|
||||
retention-days: 5
|
||||
|
||||
publish:
|
||||
name: "Publish"
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
needs: ["build"]
|
||||
permissions:
|
||||
id-token: write
|
||||
runs-on: "ubuntu-latest"
|
||||
environment:
|
||||
name: "publish"
|
||||
|
||||
steps:
|
||||
- name: "Download dists"
|
||||
uses: "actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3" # v8.0.0
|
||||
with:
|
||||
artifact-ids: ${{ needs.build.outputs.artifact-id }}
|
||||
path: "dist/"
|
||||
|
||||
- name: "Publish dists to PyPI"
|
||||
uses: "pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b" # v1.14.0
|
||||
with:
|
||||
attestations: true
|
||||
|
||||
publish-test-pypi:
|
||||
name: "Publish to Test PyPI"
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
needs: ["build"]
|
||||
permissions:
|
||||
id-token: write
|
||||
runs-on: "ubuntu-latest"
|
||||
environment:
|
||||
name: "testpypi"
|
||||
|
||||
steps:
|
||||
- name: "Download dists"
|
||||
uses: "actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3" # v8.0.0
|
||||
with:
|
||||
artifact-ids: ${{ needs.build.outputs.artifact-id }}
|
||||
path: "dist/"
|
||||
|
||||
- name: "Publish dists to Test PyPI"
|
||||
uses: "pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b" # v1.14.0
|
||||
with:
|
||||
repository-url: https://test.pypi.org/legacy/
|
||||
attestations: true
|
||||
83
.github/workflows/run-tests.yml
vendored
@@ -1,83 +0,0 @@
|
||||
name: Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t", "3.15-dev", "pypy-3.11"]
|
||||
os: [ubuntu-22.04, macOS-latest, windows-latest]
|
||||
# Pypy-3.11 can't install openssl-sys with rust
|
||||
# which prevents us from testing in GHA.
|
||||
exclude:
|
||||
- { python-version: "pypy-3.11", os: "windows-latest" }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
allow-prereleases: true
|
||||
- name: Install dependencies
|
||||
env:
|
||||
PYO3_USE_ABI3_FORWARD_COMPATIBILITY: ${{ matrix.python-version == '3.15-dev' && '1' || '' }}
|
||||
run: |
|
||||
make
|
||||
- name: Run tests
|
||||
run: |
|
||||
make ci
|
||||
|
||||
no_chardet:
|
||||
name: "No Character Detection"
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: 'Set up Python 3.10'
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
make
|
||||
python -m pip uninstall -y "charset_normalizer" "chardet"
|
||||
- name: Run tests
|
||||
run: |
|
||||
make ci
|
||||
|
||||
urllib3:
|
||||
name: 'urllib3 1.x'
|
||||
runs-on: 'ubuntu-latest'
|
||||
strategy:
|
||||
fail-fast: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: 'Set up Python 3.10'
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
make
|
||||
python -m pip install "urllib3<2"
|
||||
- name: Run tests
|
||||
run: |
|
||||
make ci
|
||||
32
.github/workflows/typecheck.yml
vendored
@@ -1,32 +0,0 @@
|
||||
name: Type Check
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
typecheck:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.10", "3.14"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install pip==26.0.1
|
||||
python -m pip install -e . --group typecheck
|
||||
|
||||
- name: Run pyright
|
||||
run: python -m pyright src/requests/
|
||||
24
.github/workflows/zizmor.yml
vendored
@@ -1,24 +0,0 @@
|
||||
# Sourced from https://github.com/zizmorcore/zizmor-action
|
||||
name: GitHub Actions Security Analysis with zizmor 🌈
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
branches: ["**"]
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
zizmor:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run zizmor 🌈
|
||||
uses: zizmorcore/zizmor-action@6599ee8b7a49aef6a770f63d261d214911a7ce02 # v0.6.0
|
||||
36
.gitignore
vendored
@@ -1,37 +1,9 @@
|
||||
.coverage
|
||||
MANIFEST
|
||||
coverage.xml
|
||||
nosetests.xml
|
||||
junit-report.xml
|
||||
pylint.txt
|
||||
toy.py
|
||||
.cache/
|
||||
cover/
|
||||
build/
|
||||
docs/_build
|
||||
requests.egg-info/
|
||||
*.pyc
|
||||
*.swp
|
||||
*.egg
|
||||
env/
|
||||
.venv/
|
||||
.eggs/
|
||||
.tox/
|
||||
.pytest_cache/
|
||||
.vscode/
|
||||
.eggs/
|
||||
|
||||
.workon
|
||||
|
||||
# in case you work with IntelliJ/PyCharm
|
||||
.idea
|
||||
*.iml
|
||||
.python-version
|
||||
|
||||
|
||||
t.py
|
||||
|
||||
t2.py
|
||||
dist
|
||||
|
||||
/.mypy_cache/
|
||||
docs/_build
|
||||
toy.py
|
||||
.gitignore
|
||||
junit-report.xml
|
||||
@@ -1,21 +0,0 @@
|
||||
exclude: 'docs/|ext/'
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-case-conflict
|
||||
- id: check-merge-conflict
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
args: [--fix=auto]
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.16.4
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: [--fix]
|
||||
- id: ruff-format
|
||||
exclude: tests/test_lowlevel.py
|
||||
@@ -1,29 +0,0 @@
|
||||
# Read the Docs configuration file for Sphinx projects
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the OS, Python version and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.12"
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
builder: "dirhtml"
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
# Optional but recommended, declare the Python requirements required
|
||||
# to build your documentation
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- path: .
|
||||
- requirements: docs/requirements.txt
|
||||
165
AUTHORS.rst
@@ -1,17 +1,17 @@
|
||||
Requests was lovingly created by Kenneth Reitz.
|
||||
Requests is written and maintained by Kenneth Reitz and
|
||||
various contributors:
|
||||
|
||||
Requests Maintainers
|
||||
````````````````````
|
||||
Development Lead
|
||||
````````````````
|
||||
|
||||
- Ian Stapleton Cordasco <graffatcolmingov@gmail.com> `@sigmavirus24 <https://github.com/sigmavirus24>`_.
|
||||
- Nate Prewitt `@nateprewitt <https://github.com/nateprewitt>`_.
|
||||
- Kenneth Reitz <me@kennethreitz.com>
|
||||
|
||||
Previous Maintainers
|
||||
````````````````````
|
||||
|
||||
- Kenneth Reitz <me@kennethreitz.org> `@kennethreitz <https://github.com/kennethreitz>`_, reluctant Keeper of the Master Crystal.
|
||||
- Cory Benfield <cory@lukasa.co.uk> `@lukasa <https://github.com/lukasa>`_
|
||||
- Seth M. Larson `@sethmlarson <https://github.com/sethmlarson>`_.
|
||||
Urllib3
|
||||
```````
|
||||
|
||||
- Andrey Petrov <andrey.petrov@shazow.net>
|
||||
|
||||
|
||||
Patches and Suggestions
|
||||
```````````````````````
|
||||
@@ -34,12 +34,12 @@ Patches and Suggestions
|
||||
- Alberto Paro
|
||||
- Jérémy Bethmont
|
||||
- 潘旭 (Xu Pan)
|
||||
- Tamás Gulácsi
|
||||
- Gulácsi Tamás
|
||||
- Rubén Abad
|
||||
- Peter Manser
|
||||
- Jeremy Selier
|
||||
- Jens Diemer
|
||||
- Alex (`@alopatin <https://github.com/alopatin>`_)
|
||||
- Alex <@alopatin>
|
||||
- Tom Hogans <tomhsx@gmail.com>
|
||||
- Armin Ronacher
|
||||
- Shrikant Sharat Kandula
|
||||
@@ -73,123 +73,26 @@ Patches and Suggestions
|
||||
- Lucian Ursu
|
||||
- Tom Moertel
|
||||
- Frank Kumro Jr
|
||||
- Mike Waldner
|
||||
- Luca De Vitis
|
||||
- digitalxero
|
||||
- Ronny Pfannschmidt
|
||||
- Chase Sterling
|
||||
- Marty Alchin
|
||||
- takluyver
|
||||
- Ben Toews (`@mastahyeti <https://github.com/mastahyeti>`_)
|
||||
- David Kemp
|
||||
- Brendon Crawford
|
||||
- Denis (`@Telofy <https://github.com/Telofy>`_)
|
||||
- Matt Giuca
|
||||
- Adam Tauber
|
||||
- Honza Javorek
|
||||
- Brendan Maguire <maguire.brendan@gmail.com>
|
||||
- Chris Dary
|
||||
- Danver Braganza <danverbraganza@gmail.com>
|
||||
- Max Countryman
|
||||
- Nick Chadwick
|
||||
- Jonathan Drosdeck
|
||||
- Jiri Machalek
|
||||
- Steve Pulec
|
||||
- Michael Kelly
|
||||
- Michael Newman <newmaniese@gmail.com>
|
||||
- Jonty Wareing <jonty@jonty.co.uk>
|
||||
- Shivaram Lingamneni
|
||||
- Miguel Turner
|
||||
- Rohan Jain (`@crodjer <https://github.com/crodjer>`_)
|
||||
- Justin Barber <barber.justin@gmail.com>
|
||||
- Roman Haritonov (`@reclosedev <https://github.com/reclosedev>`_)
|
||||
- Josh Imhoff <joshimhoff13@gmail.com>
|
||||
- Arup Malakar <amalakar@gmail.com>
|
||||
- Danilo Bargen (`@dbrgn <https://github.com/dbrgn>`_)
|
||||
- Torsten Landschoff
|
||||
- Michael Holler (`@apotheos <https://github.com/apotheos>`_)
|
||||
- Timnit Gebru
|
||||
- Sarah Gonzalez
|
||||
- Victoria Mo
|
||||
- Leila Muhtasib
|
||||
- Matthias Rahlf <matthias@webding.de>
|
||||
- Jakub Roztocil <jakub@roztocil.name>
|
||||
- Rhys Elsmore
|
||||
- André Graf (`@dergraf <https://github.com/dergraf>`_)
|
||||
- Stephen Zhuang (`@everbird <https://github.com/everbird>`_)
|
||||
- Martijn Pieters
|
||||
- Jonatan Heyman
|
||||
- David Bonner <dbonner@gmail.com> (`@rascalking <https://github.com/rascalking>`_)
|
||||
- Vinod Chandru
|
||||
- Johnny Goodnow <j.goodnow29@gmail.com>
|
||||
- Denis Ryzhkov <denisr@denisr.com>
|
||||
- Wilfred Hughes <me@wilfred.me.uk>
|
||||
- Dmitry Medvinsky <me@dmedvinsky.name>
|
||||
- Bryce Boe <bbzbryce@gmail.com> (`@bboe <https://github.com/bboe>`_)
|
||||
- Colin Dunklau <colin.dunklau@gmail.com> (`@cdunklau <https://github.com/cdunklau>`_)
|
||||
- Bob Carroll <bob.carroll@alum.rit.edu> (`@rcarz <https://github.com/rcarz>`_)
|
||||
- Hugo Osvaldo Barrera <hugo@barrera.io> (`@hobarrera <https://github.com/hobarrera>`_)
|
||||
- Łukasz Langa <lukasz@langa.pl>
|
||||
- Dave Shawley <daveshawley@gmail.com>
|
||||
- James Clarke (`@jam <https://github.com/jam>`_)
|
||||
- Kevin Burke <kev@inburke.com>
|
||||
- Flavio Curella
|
||||
- David Pursehouse <david.pursehouse@gmail.com> (`@dpursehouse <https://github.com/dpursehouse>`_)
|
||||
- Jon Parise (`@jparise <https://github.com/jparise>`_)
|
||||
- Alexander Karpinsky (`@homm86 <https://twitter.com/homm86>`_)
|
||||
- Marc Schlaich (`@schlamar <https://github.com/schlamar>`_)
|
||||
- Park Ilsu <daftonshady@gmail.com> (`@daftshady <https://github.com/daftshady>`_)
|
||||
- Matt Spitz (`@mattspitz <https://github.com/mattspitz>`_)
|
||||
- Vikram Oberoi (`@voberoi <https://github.com/voberoi>`_)
|
||||
- Can Ibanoglu <can.ibanoglu@gmail.com> (`@canibanoglu <https://github.com/canibanoglu>`_)
|
||||
- Thomas Weißschuh <thomas@t-8ch.de> (`@t-8ch <https://github.com/t-8ch>`_)
|
||||
- Jayson Vantuyl <jayson@aggressive.ly>
|
||||
- Pengfei.X <pengphy@gmail.com>
|
||||
- Kamil Madac <kamil.madac@gmail.com>
|
||||
- Michael Becker <mike@beckerfuffle.com> (`@beckerfuffle <https://twitter.com/beckerfuffle>`_)
|
||||
- Erik Wickstrom <erik@erikwickstrom.com> (`@erikwickstrom <https://github.com/erikwickstrom>`_)
|
||||
- Константин Подшумок (`@podshumok <https://github.com/podshumok>`_)
|
||||
- Ben Bass (`@codedstructure <https://github.com/codedstructure>`_)
|
||||
- Jonathan Wong <evolutionace@gmail.com> (`@ContinuousFunction <https://github.com/ContinuousFunction>`_)
|
||||
- Martin Jul (`@mjul <https://github.com/mjul>`_)
|
||||
- Joe Alcorn (`@buttscicles <https://github.com/buttscicles>`_)
|
||||
- Syed Suhail Ahmed <ssuhail.ahmed93@gmail.com> (`@syedsuhail <https://github.com/syedsuhail>`_)
|
||||
- Scott Sadler (`@ssadler <https://github.com/ssadler>`_)
|
||||
- Arthur Darcet (`@arthurdarcet <https://github.com/arthurdarcet>`_)
|
||||
- Ulrich Petri (`@ulope <https://github.com/ulope>`_)
|
||||
- Muhammad Yasoob Ullah Khalid <yasoob.khld@gmail.com> (`@yasoob <https://github.com/yasoob>`_)
|
||||
- Paul van der Linden (`@pvanderlinden <https://github.com/pvanderlinden>`_)
|
||||
- Colin Dickson (`@colindickson <https://github.com/colindickson>`_)
|
||||
- Smiley Barry (`@smiley <https://github.com/smiley>`_)
|
||||
- Shagun Sodhani (`@shagunsodhani <https://github.com/shagunsodhani>`_)
|
||||
- Robin Linderborg (`@vienno <https://github.com/vienno>`_)
|
||||
- Brian Samek (`@bsamek <https://github.com/bsamek>`_)
|
||||
- Dmitry Dygalo (`@Stranger6667 <https://github.com/Stranger6667>`_)
|
||||
- piotrjurkiewicz
|
||||
- Jesse Shapiro <jesse@jesseshapiro.net> (`@haikuginger <https://github.com/haikuginger>`_)
|
||||
- Nate Prewitt <nate.prewitt@gmail.com> (`@nateprewitt <https://github.com/nateprewitt>`_)
|
||||
- Maik Himstedt
|
||||
- Michael Hunsinger
|
||||
- Brian Bamsch <bbamsch32@gmail.com> (`@bbamsch <https://github.com/bbamsch>`_)
|
||||
- Om Prakash Kumar <omprakash070@gmail.com> (`@iamprakashom <https://github.com/iamprakashom>`_)
|
||||
- Philipp Konrad <gardiac2002@gmail.com> (`@gardiac2002 <https://github.com/gardiac2002>`_)
|
||||
- Hussain Tamboli <hussaintamboli18@gmail.com> (`@hussaintamboli <https://github.com/hussaintamboli>`_)
|
||||
- Casey Davidson (`@davidsoncasey <https://github.com/davidsoncasey>`_)
|
||||
- Andrii Soldatenko (`@a_soldatenko <https://github.com/andriisoldatenko>`_)
|
||||
- Moinuddin Quadri <moin18@gmail.com> (`@moin18 <https://github.com/moin18>`_)
|
||||
- Matt Kohl (`@mattkohl <https://github.com/mattkohl>`_)
|
||||
- Jonathan Vanasco (`@jvanasco <https://github.com/jvanasco>`_)
|
||||
- David Fontenot (`@davidfontenot <https://github.com/davidfontenot>`_)
|
||||
- Shmuel Amar (`@shmuelamar <https://github.com/shmuelamar>`_)
|
||||
- Gary Wu (`@garywu <https://github.com/garywu>`_)
|
||||
- Ryan Pineo (`@ryanpineo <https://github.com/ryanpineo>`_)
|
||||
- Ed Morley (`@edmorley <https://github.com/edmorley>`_)
|
||||
- Matt Liu <liumatt@gmail.com> (`@mlcrazy <https://github.com/mlcrazy>`_)
|
||||
- Taylor Hoff <primdevs@protonmail.com> (`@PrimordialHelios <https://github.com/PrimordialHelios>`_)
|
||||
- Arthur Vigil (`@ahvigil <https://github.com/ahvigil>`_)
|
||||
- Nehal J Wani (`@nehaljwani <https://github.com/nehaljwani>`_)
|
||||
- Demetrios Bairaktaris (`@DemetriosBairaktaris <https://github.com/demetriosbairaktaris>`_)
|
||||
- Darren Dormer (`@ddormer <https://github.com/ddormer>`_)
|
||||
- Rajiv Mayani (`@mayani <https://github.com/mayani>`_)
|
||||
- Antti Kaihola (`@akaihola <https://github.com/akaihola>`_)
|
||||
- "Dull Bananas" <dull.bananas0@gmail.com> (`@dullbananas <https://github.com/dullbananas>`_)
|
||||
- Alessio Izzo (`@aless10 <https://github.com/aless10>`_)
|
||||
- Sylvain Marié (`@smarie <https://github.com/smarie>`_)
|
||||
- Hod Bin Noon (`@hodbn <https://github.com/hodbn>`_)
|
||||
- Mike Fiedler (`@miketheman <https://github.com/miketheman>`_)
|
||||
- Andrey Petrov
|
||||
- Michael Van Veen
|
||||
- Devin Sevilla
|
||||
- Douglas Morrison
|
||||
- jbergstroem
|
||||
- jbrendel
|
||||
- Mark Story
|
||||
- Nick Hatch
|
||||
- Pat Nakajima
|
||||
- Piotr Dobrogost
|
||||
- Raymond Penners
|
||||
- Bryan Berg
|
||||
- Shane McDonald
|
||||
- Simon Sapin
|
||||
- Clermont
|
||||
- Ben Edwards
|
||||
- verm666
|
||||
- Juarez Bochi
|
||||
|
||||
2102
HISTORY.md
342
HISTORY.rst
Normal file
@@ -0,0 +1,342 @@
|
||||
History
|
||||
-------
|
||||
|
||||
0.9.2 (2012-01-18)
|
||||
++++++++++++++++++
|
||||
|
||||
* Asyncronous async.send method.
|
||||
* SSL verify=False bugfix (apparent on windows machines).
|
||||
* Support for proper chunk streams with boundaries.
|
||||
* session argument for Session classes.
|
||||
* Print entire hook tracebacks, not just exception instance.
|
||||
* Fix response.iter_lines from pending next line.
|
||||
* Fix but in HTTP-digest auth w/ URI having query strings.
|
||||
* Fix in Event Hooks section.
|
||||
* Urllib3 update.
|
||||
|
||||
|
||||
0.9.1 (2012-01-06)
|
||||
++++++++++++++++++
|
||||
|
||||
* danger_mode for automatic Response.raise_for_status()
|
||||
* Response.iter_lines refator
|
||||
|
||||
0.9.0 (2011-12-28)
|
||||
++++++++++++++++++
|
||||
|
||||
* verify ssl is default.
|
||||
|
||||
|
||||
0.8.9 (2011-12-28)
|
||||
++++++++++++++++++
|
||||
|
||||
* Packaging fix.
|
||||
|
||||
|
||||
0.8.8 (2011-12-28)
|
||||
++++++++++++++++++
|
||||
|
||||
* SSL CERT VERIFICATION!
|
||||
* Release of Cerifi: Mozilla's cert list.
|
||||
* New 'verify' argument for SSL requests.
|
||||
* Urllib3 update.
|
||||
|
||||
0.8.7 (2011-12-24)
|
||||
++++++++++++++++++
|
||||
|
||||
* iter_lines last-line truncation fix
|
||||
* Force safe_mode for async requests
|
||||
* Handle safe_mode exceptions more consistently
|
||||
* Fix iteration on null responses in safe_mode
|
||||
|
||||
0.8.6 (2011-12-18)
|
||||
++++++++++++++++++
|
||||
|
||||
* Socket timeout fixes.
|
||||
* Proxy Authorization support.
|
||||
|
||||
0.8.5 (2011-12-14)
|
||||
++++++++++++++++++
|
||||
|
||||
* Response.iter_lines!
|
||||
|
||||
0.8.4 (2011-12-11)
|
||||
++++++++++++++++++
|
||||
|
||||
* Prefetch bugfix.
|
||||
* Added license to installed version.
|
||||
|
||||
0.8.3 (2011-11-27)
|
||||
++++++++++++++++++
|
||||
|
||||
* Converted auth system to use simpler callable objects.
|
||||
* New session parameter to API methods.
|
||||
* Display full URL while logging.
|
||||
|
||||
0.8.2 (2011-11-19)
|
||||
++++++++++++++++++
|
||||
|
||||
* New unicode decoding system, based on overridable `Response.encoding`.
|
||||
* Proper URL slash-quote handling.
|
||||
* Cookies with ``[``, ``]``, and ``_`` allowed.
|
||||
|
||||
0.8.1 (2011-11-15)
|
||||
++++++++++++++++++
|
||||
|
||||
* URL Request path fix
|
||||
* Proxy fix.
|
||||
* Timeouts fix.
|
||||
|
||||
0.8.0 (2011-11-13)
|
||||
++++++++++++++++++
|
||||
|
||||
* Keep-alive support!
|
||||
* Complete removal of Urllib2
|
||||
* Complete removal of Poster
|
||||
* Complete removal of CookieJars
|
||||
* New ConnectionError raising
|
||||
* Safe_mode for error catching
|
||||
* prefetch parameter for request methods
|
||||
* OPTION method
|
||||
* Async pool size throttling
|
||||
* File uploads send real names
|
||||
* Vendored in urllib3
|
||||
|
||||
0.7.6 (2011-11-07)
|
||||
++++++++++++++++++
|
||||
|
||||
* Digest authentication bugfix (attach query data to path)
|
||||
|
||||
0.7.5 (2011-11-04)
|
||||
++++++++++++++++++
|
||||
|
||||
* Response.content = None if there was an invalid repsonse.
|
||||
* Redirection auth handling.
|
||||
|
||||
0.7.4 (2011-10-26)
|
||||
++++++++++++++++++
|
||||
|
||||
* Sesion Hooks fix.
|
||||
|
||||
0.7.3 (2011-10-23)
|
||||
++++++++++++++++++
|
||||
|
||||
* Digest Auth fix.
|
||||
|
||||
|
||||
0.7.2 (2011-10-23)
|
||||
++++++++++++++++++
|
||||
|
||||
* PATCH Fix.
|
||||
|
||||
|
||||
0.7.1 (2011-10-23)
|
||||
++++++++++++++++++
|
||||
|
||||
* Move away from urllib2 authentication handling.
|
||||
* Fully Remove AuthManager, AuthObject, &c.
|
||||
* New tuple-based auth system with handler callbacks.
|
||||
|
||||
|
||||
0.7.0 (2011-10-22)
|
||||
++++++++++++++++++
|
||||
|
||||
* Sessions are now the primary interface.
|
||||
* Deprecated InvalidMethodException.
|
||||
* PATCH fix.
|
||||
* New config system (no more global settings).
|
||||
|
||||
|
||||
0.6.6 (2011-10-19)
|
||||
++++++++++++++++++
|
||||
|
||||
* Session parameter bugfix (params merging).
|
||||
|
||||
|
||||
0.6.5 (2011-10-18)
|
||||
++++++++++++++++++
|
||||
|
||||
* Offline (fast) test suite.
|
||||
* Session dictionary argument merging.
|
||||
|
||||
|
||||
0.6.4 (2011-10-13)
|
||||
++++++++++++++++++
|
||||
|
||||
* Automatic decoding of unicode, based on HTTP Headers.
|
||||
* New ``decode_unicode`` setting.
|
||||
* Removal of ``r.read/close`` methods.
|
||||
* New ``r.faw`` interface for advanced response usage.*
|
||||
* Automatic expansion of parameterized headers.
|
||||
|
||||
|
||||
0.6.3 (2011-10-13)
|
||||
++++++++++++++++++
|
||||
|
||||
* Beautiful ``requests.async`` module, for making async requests w/ gevent.
|
||||
|
||||
|
||||
0.6.2 (2011-10-09)
|
||||
++++++++++++++++++
|
||||
|
||||
* GET/HEAD obeys allow_redirects=False.
|
||||
|
||||
|
||||
0.6.1 (2011-08-20)
|
||||
++++++++++++++++++
|
||||
|
||||
* Enhanced status codes experience ``\o/``
|
||||
* Set a maximum number of redirects (``settings.max_redirects``)
|
||||
* Full Unicode URL support
|
||||
* Support for protocol-less redirects.
|
||||
* Allow for arbitrary request types.
|
||||
* Bugfixes
|
||||
|
||||
|
||||
0.6.0 (2011-08-17)
|
||||
++++++++++++++++++
|
||||
|
||||
* New callback hook system
|
||||
* New persistient sessions object and context manager
|
||||
* Transparent Dict-cookie handling
|
||||
* Status code reference object
|
||||
* Removed Response.cached
|
||||
* Added Response.request
|
||||
* All args are kwargs
|
||||
* Relative redirect support
|
||||
* HTTPError handling improvements
|
||||
* Improved https testing
|
||||
* Bugfixes
|
||||
|
||||
|
||||
0.5.1 (2011-07-23)
|
||||
++++++++++++++++++
|
||||
|
||||
* International Domain Name Support!
|
||||
* Access headers without fetching entire body (``read()``)
|
||||
* Use lists as dicts for parameters
|
||||
* Add Forced Basic Authentication
|
||||
* Forced Basic is default authentication type
|
||||
* ``python-requests.org`` default User-Agent header
|
||||
* CaseInsensitiveDict lower-case caching
|
||||
* Response.history bugfix
|
||||
|
||||
|
||||
0.5.0 (2011-06-21)
|
||||
++++++++++++++++++
|
||||
|
||||
* PATCH Support
|
||||
* Support for Proxies
|
||||
* HTTPBin Test Suite
|
||||
* Redirect Fixes
|
||||
* settings.verbose stream writing
|
||||
* Querystrings for all methods
|
||||
* URLErrors (Connection Refused, Timeout, Invalid URLs) are treated as explicity raised
|
||||
``r.requests.get('hwe://blah'); r.raise_for_status()``
|
||||
|
||||
|
||||
0.4.1 (2011-05-22)
|
||||
++++++++++++++++++
|
||||
|
||||
* Improved Redirection Handling
|
||||
* New 'allow_redirects' param for following non-GET/HEAD Redirects
|
||||
* Settings module refactoring
|
||||
|
||||
|
||||
0.4.0 (2011-05-15)
|
||||
++++++++++++++++++
|
||||
|
||||
* Response.history: list of redirected responses
|
||||
* Case-Insensitive Header Dictionaries!
|
||||
* Unicode URLs
|
||||
|
||||
|
||||
0.3.4 (2011-05-14)
|
||||
++++++++++++++++++
|
||||
|
||||
* Urllib2 HTTPAuthentication Recursion fix (Basic/Digest)
|
||||
* Internal Refactor
|
||||
* Bytes data upload Bugfix
|
||||
|
||||
|
||||
|
||||
0.3.3 (2011-05-12)
|
||||
++++++++++++++++++
|
||||
|
||||
* Request timeouts
|
||||
* Unicode url-encoded data
|
||||
* Settings context manager and module
|
||||
|
||||
|
||||
0.3.2 (2011-04-15)
|
||||
++++++++++++++++++
|
||||
|
||||
* Automatic Decompression of GZip Encoded Content
|
||||
* AutoAuth Support for Tupled HTTP Auth
|
||||
|
||||
|
||||
0.3.1 (2011-04-01)
|
||||
++++++++++++++++++
|
||||
|
||||
* Cookie Changes
|
||||
* Response.read()
|
||||
* Poster fix
|
||||
|
||||
|
||||
0.3.0 (2011-02-25)
|
||||
++++++++++++++++++
|
||||
|
||||
* Automatic Authentication API Change
|
||||
* Smarter Query URL Parameterization
|
||||
* Allow file uploads and POST data together
|
||||
* New Authentication Manager System
|
||||
- Simpler Basic HTTP System
|
||||
- Supports all build-in urllib2 Auths
|
||||
- Allows for custom Auth Handlers
|
||||
|
||||
|
||||
0.2.4 (2011-02-19)
|
||||
++++++++++++++++++
|
||||
|
||||
* Python 2.5 Support
|
||||
* PyPy-c v1.4 Support
|
||||
* Auto-Authentication tests
|
||||
* Improved Request object constructor
|
||||
|
||||
0.2.3 (2011-02-15)
|
||||
++++++++++++++++++
|
||||
|
||||
* New HTTPHandling Methods
|
||||
- Reponse.__nonzero__ (false if bad HTTP Status)
|
||||
- Response.ok (True if expected HTTP Status)
|
||||
- Response.error (Logged HTTPError if bad HTTP Status)
|
||||
- Reponse.raise_for_status() (Raises stored HTTPError)
|
||||
|
||||
|
||||
0.2.2 (2011-02-14)
|
||||
++++++++++++++++++
|
||||
|
||||
* Still handles request in the event of an HTTPError. (Issue #2)
|
||||
* Eventlet and Gevent Monkeypatch support.
|
||||
* Cookie Support (Issue #1)
|
||||
|
||||
|
||||
0.2.1 (2011-02-14)
|
||||
++++++++++++++++++
|
||||
|
||||
* Added file attribute to POST and PUT requests for multipart-encode file uploads.
|
||||
* Added Request.url attribute for context and redirects
|
||||
|
||||
|
||||
0.2.0 (2011-02-14)
|
||||
++++++++++++++++++
|
||||
|
||||
* Birth!
|
||||
|
||||
|
||||
0.0.1 (2011-02-13)
|
||||
++++++++++++++++++
|
||||
|
||||
* Frustration
|
||||
* Conception
|
||||
|
||||
184
LICENSE
@@ -1,175 +1,13 @@
|
||||
Copyright (c) 2012 Kenneth Reitz.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
@@ -1,3 +1 @@
|
||||
include README.md LICENSE NOTICE HISTORY.md requirements-dev.txt
|
||||
recursive-include tests *.py
|
||||
recursive-include tests/certs *
|
||||
include README.rst LICENSE NOTICE HISTORY.rst test_requests.py
|
||||
|
||||
36
Makefile
@@ -1,27 +1,25 @@
|
||||
.PHONY: docs
|
||||
init:
|
||||
python -m pip install -r requirements-dev.txt
|
||||
pip install -r requirements.txt
|
||||
|
||||
test:
|
||||
python -m pytest tests
|
||||
nosetests --with-color test_requests.py
|
||||
|
||||
ci:
|
||||
python -m pytest tests --junitxml=report.xml
|
||||
ci: init
|
||||
nosetests test_requests.py --with-xunit --xunit-file=junit-report.xml
|
||||
|
||||
test-readme:
|
||||
python setup.py check --restructuredtext --strict && ([ $$? -eq 0 ] && echo "README.rst and HISTORY.rst ok") || echo "Invalid markup in README.rst or HISTORY.rst!"
|
||||
stats:
|
||||
pyflakes requests | awk -F\: '{printf "%s:%s: [E]%s\n", $1, $2, $3}' > violations.pyflakes.txt
|
||||
|
||||
coverage:
|
||||
python -m pytest --cov-config .coveragerc --verbose --cov-report term --cov-report xml --cov=src/requests tests
|
||||
site:
|
||||
cd docs; make dirhtml
|
||||
|
||||
.publishenv:
|
||||
python -m venv .publishenv
|
||||
.publishenv/bin/pip install 'twine>=1.5.0' build
|
||||
pyc:
|
||||
find . -name "*.pyc" -exec rm '{}' ';'
|
||||
|
||||
publish: .publishenv
|
||||
.publishenv/bin/python -m build
|
||||
.publishenv/bin/python -m twine upload --skip-existing dist/*
|
||||
rm -fr build dist .egg requests.egg-info
|
||||
deps:
|
||||
rm -fr requests/packages/urllib3
|
||||
git clone https://github.com/shazow/urllib3.git
|
||||
mv urllib3/urllib3 requests/packages/
|
||||
rm -fr urllib3
|
||||
|
||||
docs:
|
||||
cd docs && make html
|
||||
@echo "\033[95m\n\nBuild successful! View the docs homepage at docs/_build/html/index.html.\n\033[0m"
|
||||
docs: site
|
||||
|
||||
27
NOTICE
@@ -1,2 +1,25 @@
|
||||
Requests
|
||||
Copyright 2019 Kenneth Reitz
|
||||
Request includes some vendorized python libraries to ease installation.
|
||||
|
||||
Urllib3 License
|
||||
===============
|
||||
|
||||
This is the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt),
|
||||
Modifications copyright 2022 Kenneth Reitz.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
software and associated documentation files (the "Software"), to deal in the Software
|
||||
without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
76
README.md
@@ -1,76 +0,0 @@
|
||||
# Requests
|
||||
|
||||
[](https://pypi.org/project/requests/)
|
||||
[](https://pypi.org/project/requests)
|
||||
[](https://pepy.tech/project/requests)
|
||||
[](https://github.com/psf/requests/graphs/contributors)
|
||||
[](https://requests.readthedocs.io)
|
||||
|
||||
**Requests** is a simple, yet elegant, HTTP library.
|
||||
|
||||
```python
|
||||
>>> import requests
|
||||
>>> r = requests.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass'))
|
||||
>>> r.status_code
|
||||
200
|
||||
>>> r.headers['content-type']
|
||||
'application/json; charset=utf8'
|
||||
>>> r.encoding
|
||||
'utf-8'
|
||||
>>> r.text
|
||||
'{"authenticated": true, ...'
|
||||
>>> r.json()
|
||||
{'authenticated': True, ...}
|
||||
```
|
||||
|
||||
Requests allows you to send HTTP/1.1 requests extremely easily. There’s no need to manually add query strings to your URLs, or to form-encode your `PUT` & `POST` data — but nowadays, just use the `json` method!
|
||||
|
||||
Requests is one of the most downloaded Python packages today, pulling in around `300M downloads / week` — according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `4,000,000+` repositories.
|
||||
|
||||
## Installing Requests and Supported Versions
|
||||
|
||||
Requests is available on PyPI:
|
||||
|
||||
```console
|
||||
$ python -m pip install requests
|
||||
```
|
||||
|
||||
Requests officially supports Python 3.10+.
|
||||
|
||||
## Supported Features & Best–Practices
|
||||
|
||||
Requests is ready for the demands of building robust and reliable HTTP–speaking applications, for the needs of today.
|
||||
|
||||
- Keep-Alive & Connection Pooling
|
||||
- International Domains and URLs
|
||||
- Sessions with Cookie Persistence
|
||||
- Browser-style TLS/SSL Verification
|
||||
- Basic & Digest Authentication
|
||||
- Familiar `dict`–like Cookies
|
||||
- Automatic Content Decompression and Decoding
|
||||
- Multi-part File Uploads
|
||||
- SOCKS Proxy Support
|
||||
- Connection Timeouts
|
||||
- Streaming Downloads
|
||||
- Automatic honoring of `.netrc`
|
||||
- Chunked HTTP Requests
|
||||
|
||||
## Cloning the repository
|
||||
|
||||
When cloning the Requests repository, you may need to add the `-c
|
||||
fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit timestamp (see
|
||||
[this issue](https://github.com/psf/requests/issues/2690) for more background):
|
||||
|
||||
```shell
|
||||
git clone -c fetch.fsck.badTimezone=ignore https://github.com/psf/requests.git
|
||||
```
|
||||
|
||||
You can also apply this setting to your global Git config:
|
||||
|
||||
```shell
|
||||
git config --global fetch.fsck.badTimezone ignore
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
[](https://kennethreitz.org) [](https://www.python.org/psf)
|
||||
104
README.rst
Normal file
@@ -0,0 +1,104 @@
|
||||
Requests: HTTP for Humans
|
||||
=========================
|
||||
|
||||
Requests is an ISC Licensed HTTP library, written in Python, for human
|
||||
beings.
|
||||
|
||||
Most existing Python modules for sending HTTP requests are extremely
|
||||
verbose and cumbersome. Python's builtin urllib2 module provides most of
|
||||
the HTTP capabilities you should need, but the api is thoroughly broken.
|
||||
It requires an enormous amount of work (even method overrides) to
|
||||
perform the simplest of tasks.
|
||||
|
||||
Things shouldn't be this way. Not in Python.
|
||||
|
||||
::
|
||||
|
||||
>>> r = requests.get('https://api.github.com', auth=('user', 'pass'))
|
||||
>>> r.status_code
|
||||
204
|
||||
>>> r.headers['content-type']
|
||||
'application/json'
|
||||
>>> r.content
|
||||
...
|
||||
|
||||
See `the same code, without Requests <https://gist.github.com/973705>`_.
|
||||
|
||||
Requests allow you to send **HEAD**, **GET**, **POST**, **PUT**,
|
||||
**PATCH**, and **DELETE** HTTP requests. You can add headers, form data,
|
||||
multipart files, and parameters with simple Python dictionaries, and access the
|
||||
response data in the same way. It's powered by httplib and `urllib3
|
||||
<https://github.com/shazow/urllib3>`_, but it does all the hard work and crazy
|
||||
hacks for you.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Browser standard SSL verification.
|
||||
- Extremely simple HEAD, GET, POST, PUT, PATCH, DELETE Requests.
|
||||
- Gevent support for Asyncronous Requests.
|
||||
- Sessions with cookie persistence.
|
||||
- Basic, Digest, and Custom Authentication support.
|
||||
- Automatic form-encoding of dictionaries
|
||||
- A simple dictionary interface for request/response cookies.
|
||||
- Multipart file uploads.
|
||||
- Automatc decoding of Unicode, gzip, and deflate responses.
|
||||
- Full support for unicode URLs and domain names.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
It couldn't be simpler::
|
||||
|
||||
>>> import requests
|
||||
>>> r = requests.get('http://google.com')
|
||||
|
||||
|
||||
HTTPS? Basic Authentication? ::
|
||||
|
||||
>>> r = requests.get('https://httpbin.ep.io/basic-auth/user/pass')
|
||||
>>> r.status_code
|
||||
401
|
||||
|
||||
|
||||
Uh oh, we're not authorized! Let's add authentication. ::
|
||||
|
||||
>>> r = requests.get('https://httpbin.ep.io/basic-auth/user/pass', auth=('user', 'pass'))
|
||||
|
||||
>>> r.status_code
|
||||
200
|
||||
|
||||
>>> r.headers['content-type']
|
||||
'application/json'
|
||||
|
||||
>>> r.content
|
||||
'{"authenticated": true, "user": "user"}'
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To install requests, simply: ::
|
||||
|
||||
$ pip install requests
|
||||
|
||||
Or, if you absolutely must: ::
|
||||
|
||||
$ easy_install requests
|
||||
|
||||
But, you really shouldn't do that.
|
||||
|
||||
|
||||
|
||||
Contribute
|
||||
----------
|
||||
|
||||
#. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug. There is a Contributor Friendly tag for issues that should be ideal for people who are not very familiar with the codebase yet.
|
||||
#. Fork `the repository`_ on Github to start making your changes to the **develop** branch (or branch off of it).
|
||||
#. Write a test which shows that the bug was fixed or that the feature works as expected.
|
||||
#. Send a pull request and bug the maintainer until it gets merged and published. :) Make sure to add yourself to AUTHORS_.
|
||||
|
||||
.. _`the repository`: http://github.com/kennethreitz/requests
|
||||
.. _AUTHORS: http://github.com/kennethreitz/requests/blob/master/AUTHORS
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
1
docs/MANIFEST.in
Normal file
@@ -0,0 +1 @@
|
||||
include HISTORY.rst README.rst LICENSE
|
||||
@@ -7,11 +7,6 @@ SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://www.sphinx-doc.org/)
|
||||
endif
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
@@ -19,7 +14,8 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@@ -29,66 +25,53 @@ help:
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " applehelp to make an Apple Help Book"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@echo " coverage to run coverage check of the documentation (if enabled)"
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
.PHONY: html
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
.PHONY: dirhtml
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
.PHONY: singlehtml
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
.PHONY: pickle
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
.PHONY: json
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
.PHONY: htmlhelp
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
.PHONY: qthelp
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@@ -98,16 +81,6 @@ qthelp:
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Requests.qhc"
|
||||
|
||||
.PHONY: applehelp
|
||||
applehelp:
|
||||
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
|
||||
@echo
|
||||
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
|
||||
@echo "N.B. You won't be able to view it unless you put it in" \
|
||||
"~/Library/Documentation/Help or install it in your application" \
|
||||
"bundle."
|
||||
|
||||
.PHONY: devhelp
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@@ -117,13 +90,11 @@ devhelp:
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Requests"
|
||||
@echo "# devhelp"
|
||||
|
||||
.PHONY: epub
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
.PHONY: latex
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@@ -131,33 +102,22 @@ latex:
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
.PHONY: latexpdf
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
make -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: latexpdfja
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: text
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
.PHONY: man
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
.PHONY: texinfo
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@@ -165,52 +125,29 @@ texinfo:
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
.PHONY: info
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
.PHONY: gettext
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
.PHONY: changes
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
.PHONY: linkcheck
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
.PHONY: doctest
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
|
||||
@echo "Testing of coverage in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/coverage/python.txt."
|
||||
|
||||
.PHONY: xml
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
.PHONY: pseudoxml
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
|
||||
12
docs/_static/custom.css
vendored
@@ -1,12 +0,0 @@
|
||||
img.logo {margin-left: -20px!important;}
|
||||
|
||||
div#searchbox h3 {text-transform: capitalize;}
|
||||
|
||||
div.document {width: 1008px;}
|
||||
|
||||
div.highlight pre {padding: 11px 14px;}
|
||||
|
||||
@media screen and (max-width: 1008px) {
|
||||
div.document {width: 100%!important;}
|
||||
div.highlight pre {margin-right: -30px;}
|
||||
}
|
||||
BIN
docs/_static/requests-sidebar.png
vendored
|
Before Width: | Height: | Size: 299 KiB After Width: | Height: | Size: 12 KiB |
33
docs/_templates/sidebar.html
vendored
@@ -1,33 +0,0 @@
|
||||
<p class="logo">
|
||||
<a href="{{ pathto(root_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" alt="{{ _('Requests logo') }}" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=psf&repo=requests&type=watch&count=true&size=large"
|
||||
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></iframe>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ _('Requests is an elegant and simple HTTP library for Python, built for human beings.') }}
|
||||
</p>
|
||||
|
||||
<h3>{{ _('Useful Links') }}</h3>
|
||||
<ul>
|
||||
<li><a href="{{ pathto('user/quickstart') }}">{{ _('Quickstart') }}</a></li>
|
||||
<li><a href="{{ pathto('user/advanced') }}">{{ _('Advanced Usage') }}</a></li>
|
||||
<li><a href="{{ pathto('api') }}">{{ _('API Reference') }}</a></li>
|
||||
<li><a href="{{ pathto('community/updates') + '#release-history' }}">{{ _('Release History') }}</a></li>
|
||||
<li><a href="{{ pathto('dev/contributing') }}">{{ _('Contributors Guide') }}</a></li>
|
||||
|
||||
<p></p>
|
||||
|
||||
<li><a href="{{ pathto('community/recommended') }}">{{ _('Recommended Packages and Extensions') }}</a></li>
|
||||
|
||||
<p></p>
|
||||
|
||||
<li><a href="https://github.com/psf/requests">{{ _('Requests @ GitHub') }}</a></li>
|
||||
<li><a href="https://pypi.org/project/requests/">{{ _('Requests @ PyPI') }}</a></li>
|
||||
<li><a href="https://github.com/psf/requests/issues">{{ _('Issue Tracker') }}</a></li>
|
||||
</ul>
|
||||
43
docs/_templates/sidebarintro.html
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<p class="logo">
|
||||
<a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" alt="Logo"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<iframe src="http://markdotto.github.com/github-buttons/github-btn.html?user=kennethreitz&repo=requests&type=watch&count=true&size=large"
|
||||
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></iframe>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Requests is an elegant and simple HTTP library for Python, built for
|
||||
human beings. You are currently looking at the documentation of the
|
||||
development release.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>Support Requests</h3>
|
||||
<p>
|
||||
If you love Requests, consider making a small donation <a href="https://flattr.com/thing/442264/Requests">on Flattr</a>:
|
||||
</p>
|
||||
<p>
|
||||
<a class="FlattrButton" style="display:none;" rev="flattr;button:compact;" href="http://docs.python-requests.org/"></a>
|
||||
<noscript><a href="http://flattr.com/thing/442264/Requests" target="_blank">
|
||||
<img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a></noscript>
|
||||
</p>
|
||||
|
||||
<h3>Feedback</h3>
|
||||
<p>
|
||||
Feedback is greatly appreciated. If you have any questions, comments,
|
||||
random praise, or anonymous threats, <a href="mailto:me@kennethreitz.com">
|
||||
shoot me an email</a>.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>Useful Links</h3>
|
||||
<ul>
|
||||
<li><a href="http://pypi.python.org/pypi/requests">Requests @ PyPI</a></li>
|
||||
<li><a href="http://github.com/kennethreitz/requests">Requests @ GitHub</a></li>
|
||||
<li><a href="https://flattr.com/thing/442264/Requests">Requests @ Flattr</a></li>
|
||||
<li><a href="http://github.com/kennethreitz/requests/issues">Issue Tracker</a></li>
|
||||
</ul>
|
||||
25
docs/_templates/sidebarlogo.html
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<p class="logo">
|
||||
<a href="{{ pathto(master_doc) }}">
|
||||
<img class="logo" src="{{ pathto('_static/requests-sidebar.png', 1) }}" alt="Logo"/>
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<iframe src="http://markdotto.github.com/github-buttons/github-btn.html?user=kennethreitz&repo=requests&type=watch&count=true&size=large"
|
||||
allowtransparency="true" frameborder="0" scrolling="0" width="200px" height="35px"></iframe>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Requests is an elegant and simple HTTP library for Python, built for
|
||||
human beings. You are currently looking at the documentation of the
|
||||
development release.
|
||||
</p>
|
||||
|
||||
<h3>Support Requests</h3>
|
||||
<p>
|
||||
If you love Requests, consider making a small donation <a href="https://flattr.com/thing/442264/Requests">on Flattr</a>:
|
||||
</p>
|
||||
<p>
|
||||
<a class="FlattrButton" style="display:none;" rev="flattr;button:compact;" href="http://docs.python-requests.org/"></a>
|
||||
<noscript><a href="http://flattr.com/thing/442264/Requests" target="_blank">
|
||||
<img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /></a></noscript>
|
||||
</p>
|
||||
4
docs/_themes/LICENSE
vendored
@@ -1,9 +1,9 @@
|
||||
Modifications:
|
||||
Modifications:
|
||||
|
||||
Copyright (c) 2011 Kenneth Reitz.
|
||||
|
||||
|
||||
Original Project:
|
||||
Original Project:
|
||||
|
||||
Copyright (c) 2010 by Armin Ronacher.
|
||||
|
||||
|
||||
25
docs/_themes/README.rst
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
krTheme Sphinx Style
|
||||
====================
|
||||
|
||||
This repository contains sphinx styles Kenneth Reitz uses in most of
|
||||
his projects. It is a derivative of Mitsuhiko's themes for Flask and Flask related
|
||||
projects. To use this style in your Sphinx documentation, follow
|
||||
this guide:
|
||||
|
||||
1. put this folder as _themes into your docs folder. Alternatively
|
||||
you can also use git submodules to check out the contents there.
|
||||
|
||||
2. add this to your conf.py: ::
|
||||
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
html_theme_path = ['_themes']
|
||||
html_theme = 'flask'
|
||||
|
||||
The following themes exist:
|
||||
|
||||
**kr**
|
||||
the standard flask documentation theme for large projects
|
||||
|
||||
**kr_small**
|
||||
small one-page theme. Intended to be used by very small addon libraries.
|
||||
|
||||
67
docs/_themes/kr/layout.html
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
{%- extends "basic/layout.html" %}
|
||||
{%- block extrahead %}
|
||||
{{ super() }}
|
||||
{% if theme_touch_icon %}
|
||||
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
|
||||
{% endif %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
|
||||
{% endblock %}
|
||||
{%- block relbar2 %}{% endblock %}
|
||||
{%- block footer %}
|
||||
<div class="footer">
|
||||
© Copyright {{ copyright }}.
|
||||
</div>
|
||||
<a href="https://github.com/kennethreitz/requests" class="github">
|
||||
<img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" class="github"/>
|
||||
</a>
|
||||
<script type="text/javascript">
|
||||
/* <![CDATA[ */
|
||||
(function() {
|
||||
var s = document.createElement('script'), t = document.getElementsByTagName('script')[0];
|
||||
s.type = 'text/javascript';
|
||||
s.async = true;
|
||||
s.src = 'http://api.flattr.com/js/0.6/load.js?mode=auto';
|
||||
t.parentNode.insertBefore(s, t);
|
||||
})();
|
||||
/* ]]> */
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/javascript" src="//www.hellobar.com/hellobar.js"></script>
|
||||
<script type="text/javascript">
|
||||
new HelloBar(36402,48802);
|
||||
</script>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-8742933-11']);
|
||||
_gaq.push(['_setDomainName', 'none']);
|
||||
_gaq.push(['_setAllowLinker', true]);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var t = document.createElement('script');
|
||||
t.type = 'text/javascript';
|
||||
t.async = true;
|
||||
t.id = 'gauges-tracker';
|
||||
t.setAttribute('data-site-id',
|
||||
'4ddc27f6613f5d186d000007');
|
||||
t.src = '//secure.gaug.es/track.js';
|
||||
var s = document.getElementsByTagName('script')[0];
|
||||
s.parentNode.insertBefore(t, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
|
||||
{%- endblock %}
|
||||
19
docs/_themes/kr/relations.html
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
|
||||
{%- for parent in parents %}
|
||||
<li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
|
||||
{%- endfor %}
|
||||
{%- if prev %}
|
||||
<li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
|
||||
}}">{{ prev.title }}</a></li>
|
||||
{%- endif %}
|
||||
{%- if next %}
|
||||
<li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
|
||||
}}">{{ next.title }}</a></li>
|
||||
{%- endif %}
|
||||
{%- for parent in parents %}
|
||||
</ul></li>
|
||||
{%- endfor %}
|
||||
</ul></li>
|
||||
</ul>
|
||||
570
docs/_themes/kr/static/flasky.css_t
vendored
Normal file
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
* flasky.css_t
|
||||
* ~~~~~~~~~~~~
|
||||
*
|
||||
* :copyright: Copyright 2010 by Armin Ronacher. Modifications by Kenneth Reitz.
|
||||
* :license: Flask Design License, see LICENSE for details.
|
||||
*/
|
||||
|
||||
{% set page_width = '940px' %}
|
||||
{% set sidebar_width = '220px' %}
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: 'goudy old style', 'minion pro', 'bell mt', Georgia, 'Hiragino Mincho Pro';
|
||||
font-size: 17px;
|
||||
background-color: white;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: {{ page_width }};
|
||||
margin: 30px auto 0 auto;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 {{ sidebar_width }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
width: {{ sidebar_width }};
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #B1B4B6;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #ffffff;
|
||||
color: #3E4349;
|
||||
padding: 0 30px 0 30px;
|
||||
}
|
||||
|
||||
img.floatingflask {
|
||||
padding: 0 0 10px 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
width: {{ page_width }};
|
||||
margin: 20px auto 30px auto;
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.related {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted #999;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a:hover {
|
||||
border-bottom: 1px solid #999;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 18px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper p.logo {
|
||||
padding: 0;
|
||||
margin: -10px 0 0 -20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3,
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
color: #444;
|
||||
font-size: 24px;
|
||||
font-weight: normal;
|
||||
margin: 0 0 5px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo a,
|
||||
div.sphinxsidebar h3 a,
|
||||
div.sphinxsidebar p.logo a:hover,
|
||||
div.sphinxsidebar h3 a:hover {
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: #555;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px 0;
|
||||
padding: 0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #ccc;
|
||||
font-family: 'Georgia', serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
color: #004B6B;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #6D4100;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
margin: 30px 0px 10px 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
|
||||
div.body h2 { font-size: 180%; }
|
||||
div.body h3 { font-size: 150%; }
|
||||
div.body h4 { font-size: 130%; }
|
||||
div.body h5 { font-size: 100%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: #ddd;
|
||||
padding: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
color: #444;
|
||||
background: #eaeaea;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
div.admonition {
|
||||
background: #fafafa;
|
||||
margin: 20px -30px;
|
||||
padding: 10px 30px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.admonition tt.xref, div.admonition a tt {
|
||||
border-bottom: 1px solid #fafafa;
|
||||
}
|
||||
|
||||
dd div.admonition {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
font-size: 24px;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div.admonition p.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.highlight {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
dt:target, .highlight {
|
||||
background: #FAF3E8;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre, tt {
|
||||
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
}
|
||||
|
||||
tt.descname, tt.descclassname {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
padding-right: 0.08em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 1px solid #888;
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
border: 1px solid #888;
|
||||
padding: 0.25em 0.7em;
|
||||
}
|
||||
|
||||
table.field-list, table.footnote {
|
||||
border: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table.footnote {
|
||||
margin: 15px 0;
|
||||
width: 100%;
|
||||
border: 1px solid #eee;
|
||||
background: #fdfdfd;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
table.footnote + table.footnote {
|
||||
margin-top: -15px;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
table.field-list th {
|
||||
padding: 0 0.8em 0 0;
|
||||
}
|
||||
|
||||
table.field-list td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.footnote td.label {
|
||||
width: 0px;
|
||||
padding: 0.3em 0 0.3em 0.5em;
|
||||
}
|
||||
|
||||
table.footnote td {
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 0 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin: 10px 0 10px 30px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #eee;
|
||||
padding: 7px 30px;
|
||||
margin: 15px -30px;
|
||||
line-height: 1.3em;
|
||||
}
|
||||
|
||||
dl pre, blockquote pre, li pre {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
dl dl pre {
|
||||
margin-left: -90px;
|
||||
padding-left: 90px;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
color: #222;
|
||||
/* padding: 1px 2px; */
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: #FBFBFB;
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
||||
a.reference {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dotted #004B6B;
|
||||
}
|
||||
|
||||
a.reference:hover {
|
||||
border-bottom: 1px solid #6D4100;
|
||||
}
|
||||
|
||||
a.footnote-reference {
|
||||
text-decoration: none;
|
||||
font-size: 0.7em;
|
||||
vertical-align: top;
|
||||
border-bottom: 1px dotted #004B6B;
|
||||
}
|
||||
|
||||
a.footnote-reference:hover {
|
||||
border-bottom: 1px solid #6D4100;
|
||||
}
|
||||
|
||||
a:hover tt {
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 870px) {
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
margin-left: 0;
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.document {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media screen and (max-width: 875px) {
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px 30px;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: none;
|
||||
background: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
display: block;
|
||||
float: none;
|
||||
width: 102.5%;
|
||||
margin: 50px -30px -20px -30px;
|
||||
padding: 10px 20px;
|
||||
background: #333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
|
||||
div.sphinxsidebar h3 a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.logo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.document {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.related {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 10px 0 20px 0;
|
||||
}
|
||||
|
||||
div.related ul,
|
||||
div.related ul li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.body {
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.rtd_doc_footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.document {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* scrollbars */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button:start:decrement,
|
||||
::-webkit-scrollbar-button:end:increment {
|
||||
display: block;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button:vertical:increment {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track-piece {
|
||||
background-color: #eee;
|
||||
-webkit-border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:vertical {
|
||||
height: 50px;
|
||||
background-color: #ccc;
|
||||
-webkit-border-radius: 3px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:horizontal {
|
||||
width: 50px;
|
||||
background-color: #ccc;
|
||||
-webkit-border-radius: 3px;
|
||||
}
|
||||
|
||||
/* misc. */
|
||||
|
||||
.revsys-inline {
|
||||
display: none!important;
|
||||
}
|
||||
7
docs/_themes/kr/theme.conf
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = flasky.css
|
||||
pygments_style = flask_theme_support.FlaskyStyle
|
||||
|
||||
[options]
|
||||
touch_icon =
|
||||
22
docs/_themes/kr_small/layout.html
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{% extends "basic/layout.html" %}
|
||||
{% block header %}
|
||||
{{ super() }}
|
||||
{% if pagename == 'index' %}
|
||||
<div class=indexwrapper>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block footer %}
|
||||
{% if pagename == 'index' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{# do not display relbars #}
|
||||
{% block relbar1 %}{% endblock %}
|
||||
{% block relbar2 %}
|
||||
{% if theme_github_fork %}
|
||||
<a href="http://github.com/{{ theme_github_fork }}"><img style="position: fixed; top: 0; right: 0; border: 0;"
|
||||
src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block sidebar1 %}{% endblock %}
|
||||
{% block sidebar2 %}{% endblock %}
|
||||
287
docs/_themes/kr_small/static/flasky.css_t
vendored
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* flasky.css_t
|
||||
* ~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- flasky theme based on nature theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: 'Georgia', serif;
|
||||
font-size: 17px;
|
||||
color: #000;
|
||||
background: white;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 40px auto 0 auto;
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #B1B4B6;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #ffffff;
|
||||
color: #3E4349;
|
||||
padding: 0 30px 30px 30px;
|
||||
}
|
||||
|
||||
img.floatingflask {
|
||||
padding: 0 0 10px 10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
text-align: right;
|
||||
color: #888;
|
||||
padding: 10px;
|
||||
font-size: 14px;
|
||||
width: 650px;
|
||||
margin: 0 auto 40px auto;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #888;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.related {
|
||||
line-height: 32px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
padding: 0 0 0 10px;
|
||||
}
|
||||
|
||||
div.related a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
color: #004B6B;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #6D4100;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.body {
|
||||
padding-bottom: 40px; /* saved for footer */
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
margin: 30px 0px 10px 0px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
{% if theme_index_logo %}
|
||||
div.indexwrapper h1 {
|
||||
text-indent: -999999px;
|
||||
background: url({{ theme_index_logo }}) no-repeat center center;
|
||||
height: {{ theme_index_logo_height }};
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
div.body h2 { font-size: 180%; }
|
||||
div.body h3 { font-size: 150%; }
|
||||
div.body h4 { font-size: 130%; }
|
||||
div.body h5 { font-size: 100%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: white;
|
||||
padding: 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
color: #444;
|
||||
background: #eaeaea;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
div.admonition {
|
||||
background: #fafafa;
|
||||
margin: 20px -30px;
|
||||
padding: 10px 30px;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title {
|
||||
font-family: 'Garamond', 'Georgia', serif;
|
||||
font-weight: normal;
|
||||
font-size: 24px;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
div.admonition p.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.highlight{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
dt:target, .highlight {
|
||||
background: #FAF3E8;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: #ffe4e4;
|
||||
border: 1px solid #f66;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre, tt {
|
||||
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
}
|
||||
|
||||
tt.descname, tt.descclassname {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
padding-right: 0.08em;
|
||||
}
|
||||
|
||||
img.screenshot {
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 1px solid #888;
|
||||
-moz-box-shadow: 2px 2px 4px #eee;
|
||||
-webkit-box-shadow: 2px 2px 4px #eee;
|
||||
box-shadow: 2px 2px 4px #eee;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
border: 1px solid #888;
|
||||
padding: 0.25em 0.7em;
|
||||
}
|
||||
|
||||
table.field-list, table.footnote {
|
||||
border: none;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
table.footnote {
|
||||
margin: 15px 0;
|
||||
width: 100%;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
table.field-list th {
|
||||
padding: 0 0.8em 0 0;
|
||||
}
|
||||
|
||||
table.field-list td {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.footnote td {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dl dd {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 0;
|
||||
margin: 15px -30px;
|
||||
padding: 8px;
|
||||
line-height: 1.3em;
|
||||
padding: 7px 30px;
|
||||
background: #eee;
|
||||
border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
}
|
||||
|
||||
dl pre {
|
||||
margin-left: -60px;
|
||||
padding-left: 60px;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
color: #222;
|
||||
/* padding: 1px 2px; */
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: #FBFBFB;
|
||||
}
|
||||
|
||||
a:hover tt {
|
||||
background: #EEE;
|
||||
}
|
||||
10
docs/_themes/kr_small/theme.conf
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = flasky.css
|
||||
nosidebar = true
|
||||
pygments_style = flask_theme_support.FlaskyStyle
|
||||
|
||||
[options]
|
||||
index_logo = ''
|
||||
index_logo_height = 120px
|
||||
github_fork = ''
|
||||
331
docs/api.rst
@@ -1,11 +1,11 @@
|
||||
.. _api:
|
||||
|
||||
Developer Interface
|
||||
===================
|
||||
API
|
||||
===
|
||||
|
||||
.. module:: requests
|
||||
|
||||
This part of the documentation covers all the interfaces of Requests. For
|
||||
This part of the documentation covers all the interfaces of Requests. For
|
||||
parts where Requests depends on external libraries, we document the most
|
||||
important right here and provide links to the canonical documentation.
|
||||
|
||||
@@ -13,11 +13,19 @@ important right here and provide links to the canonical documentation.
|
||||
Main Interface
|
||||
--------------
|
||||
|
||||
All of Requests' functionality can be accessed by these 7 methods.
|
||||
All of Request's functionality can be accessed by these 7 methods.
|
||||
They all return an instance of the :class:`Response <Response>` object.
|
||||
|
||||
.. autofunction:: request
|
||||
|
||||
---------------------
|
||||
|
||||
|
||||
.. autoclass:: Response
|
||||
:inherited-members:
|
||||
|
||||
---------------------
|
||||
|
||||
.. autofunction:: head
|
||||
.. autofunction:: get
|
||||
.. autofunction:: post
|
||||
@@ -25,237 +33,112 @@ They all return an instance of the :class:`Response <Response>` object.
|
||||
.. autofunction:: patch
|
||||
.. autofunction:: delete
|
||||
|
||||
|
||||
-----------------
|
||||
|
||||
.. autofunction:: session
|
||||
|
||||
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
~~~~~~~~~~
|
||||
|
||||
.. autoexception:: requests.RequestException
|
||||
.. autoexception:: requests.ConnectionError
|
||||
.. autoexception:: requests.HTTPError
|
||||
.. autoexception:: requests.TooManyRedirects
|
||||
.. autoexception:: requests.ConnectTimeout
|
||||
.. autoexception:: requests.ReadTimeout
|
||||
.. autoexception:: requests.Timeout
|
||||
.. autoexception:: requests.JSONDecodeError
|
||||
.. module:: requests
|
||||
|
||||
.. autoexception:: RequestException
|
||||
.. autoexception:: ConnectionError
|
||||
.. autoexception:: HTTPError
|
||||
.. autoexception:: URLRequired
|
||||
.. autoexception:: TooManyRedirects
|
||||
|
||||
|
||||
Request Sessions
|
||||
----------------
|
||||
.. _configurations:
|
||||
|
||||
.. _sessionapi:
|
||||
Configurations
|
||||
--------------
|
||||
|
||||
.. autoclass:: Session
|
||||
.. automodule:: requests.defaults
|
||||
|
||||
|
||||
.. _async:
|
||||
|
||||
Async
|
||||
-----
|
||||
|
||||
.. module:: requests.async
|
||||
|
||||
|
||||
.. autofunction:: map
|
||||
.. autofunction:: request
|
||||
.. autofunction:: head
|
||||
.. autofunction:: get
|
||||
.. autofunction:: post
|
||||
.. autofunction:: put
|
||||
.. autofunction:: patch
|
||||
.. autofunction:: delete
|
||||
|
||||
|
||||
|
||||
Utilities
|
||||
---------
|
||||
|
||||
These functions are used internally, but may be useful outside of
|
||||
Requests.
|
||||
|
||||
.. module:: requests.utils
|
||||
|
||||
Status Code Lookup
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. autofunction:: requests.codes
|
||||
|
||||
::
|
||||
|
||||
>>> requests.codes['temporary_redirect']
|
||||
307
|
||||
|
||||
>>> requests.codes.teapot
|
||||
418
|
||||
|
||||
>>> requests.codes['\o/']
|
||||
200
|
||||
|
||||
Cookies
|
||||
~~~~~~~
|
||||
|
||||
.. autofunction:: dict_from_cookiejar
|
||||
.. autofunction:: cookiejar_from_dict
|
||||
.. autofunction:: add_dict_to_cookiejar
|
||||
|
||||
|
||||
Encodings
|
||||
~~~~~~~~~
|
||||
|
||||
.. autofunction:: get_encodings_from_content
|
||||
.. autofunction:: get_encoding_from_headers
|
||||
.. autofunction:: get_unicode_from_response
|
||||
.. autofunction:: decode_gzip
|
||||
|
||||
|
||||
Internals
|
||||
---------
|
||||
|
||||
These items are an internal component to Requests, and should never be
|
||||
seen by the end user (developer). This part of the API documentation
|
||||
exists for those who are extending the functionality of Requests.
|
||||
|
||||
|
||||
Classes
|
||||
~~~~~~~
|
||||
|
||||
.. autoclass:: requests.Response
|
||||
:inherited-members:
|
||||
|
||||
|
||||
Lower-Level Classes
|
||||
-------------------
|
||||
|
||||
.. autoclass:: requests.Request
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: Response
|
||||
:inherited-members:
|
||||
:exclude-members: is_permanent_redirect
|
||||
.. _sessionapi:
|
||||
|
||||
|
||||
Lower-Lower-Level Classes
|
||||
-------------------------
|
||||
|
||||
.. autoclass:: requests.PreparedRequest
|
||||
.. autoclass:: requests.Session
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: requests.adapters.BaseAdapter
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: requests.adapters.HTTPAdapter
|
||||
:inherited-members:
|
||||
|
||||
Authentication
|
||||
--------------
|
||||
|
||||
.. autoclass:: requests.auth.AuthBase
|
||||
.. autoclass:: requests.auth.HTTPBasicAuth
|
||||
.. autoclass:: requests.auth.HTTPProxyAuth
|
||||
.. autoclass:: requests.auth.HTTPDigestAuth
|
||||
|
||||
|
||||
|
||||
Encodings
|
||||
---------
|
||||
|
||||
.. autofunction:: requests.utils.get_encodings_from_content
|
||||
.. autofunction:: requests.utils.get_encoding_from_headers
|
||||
.. autofunction:: requests.utils.get_unicode_from_response
|
||||
|
||||
|
||||
.. _api-cookies:
|
||||
|
||||
Cookies
|
||||
-------
|
||||
|
||||
.. autofunction:: requests.utils.dict_from_cookiejar
|
||||
.. autofunction:: requests.utils.add_dict_to_cookiejar
|
||||
.. autofunction:: requests.cookies.cookiejar_from_dict
|
||||
|
||||
.. autoclass:: requests.cookies.RequestsCookieJar
|
||||
:inherited-members:
|
||||
|
||||
.. autoclass:: requests.cookies.CookieConflictError
|
||||
:inherited-members:
|
||||
|
||||
|
||||
|
||||
Status Code Lookup
|
||||
------------------
|
||||
|
||||
.. autoclass:: requests.codes
|
||||
|
||||
.. automodule:: requests.status_codes
|
||||
|
||||
|
||||
Migrating to 1.x
|
||||
----------------
|
||||
|
||||
This section details the main differences between 0.x and 1.x and is meant
|
||||
to ease the pain of upgrading.
|
||||
|
||||
|
||||
API Changes
|
||||
~~~~~~~~~~~
|
||||
|
||||
* ``Response.json`` is now a callable and not a property of a response.
|
||||
|
||||
::
|
||||
|
||||
import requests
|
||||
r = requests.get('https://api.github.com/events')
|
||||
r.json() # This *call* raises an exception if JSON decoding fails
|
||||
|
||||
* The ``Session`` API has changed. Sessions objects no longer take parameters.
|
||||
``Session`` is also now capitalized, but it can still be
|
||||
instantiated with a lowercase ``session`` for backwards compatibility.
|
||||
|
||||
::
|
||||
|
||||
s = requests.Session() # formerly, session took parameters
|
||||
s.auth = auth
|
||||
s.headers.update(headers)
|
||||
r = s.get('https://httpbin.org/headers')
|
||||
|
||||
* All request hooks have been removed except 'response'.
|
||||
|
||||
* Authentication helpers have been broken out into separate modules. See
|
||||
requests-oauthlib_ and requests-kerberos_.
|
||||
|
||||
.. _requests-oauthlib: https://github.com/requests/requests-oauthlib
|
||||
.. _requests-kerberos: https://github.com/requests/requests-kerberos
|
||||
|
||||
* The parameter for streaming requests was changed from ``prefetch`` to
|
||||
``stream`` and the logic was inverted. In addition, ``stream`` is now
|
||||
required for raw response reading.
|
||||
|
||||
::
|
||||
|
||||
# in 0.x, passing prefetch=False would accomplish the same thing
|
||||
r = requests.get('https://api.github.com/events', stream=True)
|
||||
for chunk in r.iter_content(8192):
|
||||
...
|
||||
|
||||
* The ``config`` parameter to the requests method has been removed. Some of
|
||||
these options are now configured on a ``Session`` such as keep-alive and
|
||||
maximum number of redirects. The verbosity option should be handled by
|
||||
configuring logging.
|
||||
|
||||
::
|
||||
|
||||
import requests
|
||||
import logging
|
||||
|
||||
# Enabling debugging at http.client level (requests->urllib3->http.client)
|
||||
# you will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA.
|
||||
# the only thing missing will be the response.body which is not logged.
|
||||
try: # for Python 3
|
||||
from http.client import HTTPConnection
|
||||
except ImportError:
|
||||
from httplib import HTTPConnection
|
||||
HTTPConnection.debuglevel = 1
|
||||
|
||||
logging.basicConfig() # you need to initialize logging, otherwise you will not see anything from requests
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
requests_log = logging.getLogger("urllib3")
|
||||
requests_log.setLevel(logging.DEBUG)
|
||||
requests_log.propagate = True
|
||||
|
||||
requests.get('https://httpbin.org/headers')
|
||||
|
||||
|
||||
|
||||
Licensing
|
||||
~~~~~~~~~
|
||||
|
||||
One key difference that has nothing to do with the API is a change in the
|
||||
license from the ISC_ license to the `Apache 2.0`_ license. The Apache 2.0
|
||||
license ensures that contributions to Requests are also covered by the Apache
|
||||
2.0 license.
|
||||
|
||||
.. _ISC: https://opensource.org/licenses/ISC
|
||||
.. _Apache 2.0: https://opensource.org/licenses/Apache-2.0
|
||||
|
||||
|
||||
Migrating to 2.x
|
||||
----------------
|
||||
|
||||
|
||||
Compared with the 1.0 release, there were relatively few backwards
|
||||
incompatible changes, but there are still a few issues to be aware of with
|
||||
this major release.
|
||||
|
||||
For more details on the changes in this release including new APIs, links
|
||||
to the relevant GitHub issues and some of the bug fixes, read Cory's blog_
|
||||
on the subject.
|
||||
|
||||
.. _blog: https://lukasa.co.uk/2013/09/Requests_20/
|
||||
|
||||
|
||||
API Changes
|
||||
~~~~~~~~~~~
|
||||
|
||||
* There were a couple changes to how Requests handles exceptions.
|
||||
``RequestException`` is now a subclass of ``IOError`` rather than
|
||||
``RuntimeError`` as that more accurately categorizes the type of error.
|
||||
In addition, an invalid URL escape sequence now raises a subclass of
|
||||
``RequestException`` rather than a ``ValueError``.
|
||||
|
||||
::
|
||||
|
||||
requests.get('http://%zz/') # raises requests.exceptions.InvalidURL
|
||||
|
||||
Lastly, ``httplib.IncompleteRead`` exceptions caused by incorrect chunked
|
||||
encoding will now raise a Requests ``ChunkedEncodingError`` instead.
|
||||
|
||||
* The proxy API has changed slightly. The scheme for a proxy URL is now
|
||||
required.
|
||||
|
||||
::
|
||||
|
||||
proxies = {
|
||||
"http": "10.10.1.10:3128", # use http://10.10.1.10:3128 instead
|
||||
}
|
||||
|
||||
# In requests 1.x, this was legal, in requests 2.x,
|
||||
# this raises requests.exceptions.MissingSchema
|
||||
requests.get("http://example.org", proxies=proxies)
|
||||
|
||||
|
||||
Behavioural Changes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Keys in the ``headers`` dictionary are now native strings on all Python
|
||||
versions, i.e. bytestrings on Python 2 and unicode on Python 3. If the
|
||||
keys are not native strings (unicode on Python 2 or bytestrings on Python 3)
|
||||
they will be converted to the native string type assuming UTF-8 encoding.
|
||||
|
||||
* Values in the ``headers`` dictionary should always be strings. This has
|
||||
been the project's position since before 1.0 but a recent change
|
||||
(since version 2.11.0) enforces this more strictly. It's advised to avoid
|
||||
passing header values as unicode when possible.
|
||||
|
||||
@@ -11,9 +11,6 @@ Encoded Data?
|
||||
Requests automatically decompresses gzip-encoded responses, and does
|
||||
its best to decode response content to unicode when possible.
|
||||
|
||||
When either the `brotli <https://pypi.org/project/Brotli/>`_ or `brotlicffi <https://pypi.org/project/brotlicffi/>`_
|
||||
package is installed, requests also decodes Brotli-encoded responses.
|
||||
|
||||
You can get direct access to the raw response (and even the socket),
|
||||
if needed as well.
|
||||
|
||||
@@ -22,15 +19,14 @@ Custom User-Agents?
|
||||
-------------------
|
||||
|
||||
Requests allows you to easily override User-Agent strings, along with
|
||||
any other HTTP Header. See :ref:`documentation about headers <custom-headers>`.
|
||||
|
||||
any other HTTP Header.
|
||||
|
||||
|
||||
Why not Httplib2?
|
||||
-----------------
|
||||
|
||||
Chris Adams gave an excellent summary on
|
||||
`Hacker News <https://news.ycombinator.com/item?id=2884406>`_:
|
||||
`Hacker News <http://news.ycombinator.com/item?id=2884406>`_:
|
||||
|
||||
httplib2 is part of why you should use requests: it's far more respectable
|
||||
as a client but not as well documented and it still takes way too much code
|
||||
@@ -44,8 +40,8 @@ Chris Adams gave an excellent summary on
|
||||
Disclosure: I'm listed in the requests AUTHORS file but can claim credit
|
||||
for, oh, about 0.0001% of the awesomeness.
|
||||
|
||||
1. https://code.google.com/p/httplib2/issues/detail?id=96 is a good example:
|
||||
an annoying bug that affected many people, there was a fix available for
|
||||
1. http://code.google.com/p/httplib2/issues/detail?id=96 is a good example:
|
||||
an annoying bug which affect many people, there was a fix available for
|
||||
months, which worked great when I applied it in a fork and pounded a couple
|
||||
TB of data through it, but it took over a year to make it into trunk and
|
||||
even longer to make it onto PyPI where any other project which required "
|
||||
@@ -55,36 +51,39 @@ Chris Adams gave an excellent summary on
|
||||
Python 3 Support?
|
||||
-----------------
|
||||
|
||||
Yes! Requests supports all `officially supported versions of Python <https://devguide.python.org/versions/>`_
|
||||
and recent releases of PyPy.
|
||||
It's on the way. Here's a list of Python platforms that are officially
|
||||
supported:
|
||||
|
||||
Python 2 Support?
|
||||
* cPython 2.5
|
||||
* cPython 2.5.5
|
||||
* cPython 2.5.6
|
||||
* cPython 2.6
|
||||
* cPython 2.6.6
|
||||
* cPython 2.6.7
|
||||
* cPython 2.7
|
||||
* cPython 2.7.1
|
||||
* cPython 2.7.2
|
||||
* PyPy-c 1.4
|
||||
* PyPy-c 1.5
|
||||
|
||||
|
||||
Support for Python 3.x is coming *very* soon.
|
||||
|
||||
|
||||
Keep-alive Support?
|
||||
-------------------
|
||||
|
||||
Yep!
|
||||
|
||||
|
||||
Proxy Support?
|
||||
--------------
|
||||
|
||||
You bet!
|
||||
|
||||
|
||||
SSL Verification?
|
||||
-----------------
|
||||
|
||||
No! As of Requests 2.28.0, Requests no longer supports Python 2.7. Users who
|
||||
have been unable to migrate should pin to `requests<2.28`. Full information
|
||||
can be found in `psf/requests#6023 <https://github.com/psf/requests/issues/6023>`_.
|
||||
Absolutely.
|
||||
|
||||
It is *highly* recommended users migrate to a supported Python 3.x version now since
|
||||
Python 2.7 is no longer receiving bug fixes or security updates as of January 1, 2020.
|
||||
|
||||
What are "hostname doesn't match" errors?
|
||||
-----------------------------------------
|
||||
|
||||
These errors occur when :ref:`SSL certificate verification <verification>`
|
||||
fails to match the certificate the server responds with to the hostname
|
||||
Requests thinks it's contacting. If you're certain the server's SSL setup is
|
||||
correct (for example, because you can visit the site with your browser) and
|
||||
you're using Python 2.7, a possible explanation is that you need
|
||||
Server-Name-Indication.
|
||||
|
||||
`Server-Name-Indication`_, or SNI, is an official extension to SSL where the
|
||||
client tells the server what hostname it is contacting. This is important
|
||||
when servers are using `Virtual Hosting`_. When such servers are hosting
|
||||
more than one SSL site they need to be able to return the appropriate
|
||||
certificate based on the hostname the client is connecting to.
|
||||
|
||||
Python 3 already includes native support for SNI in their SSL modules.
|
||||
|
||||
.. _`Server-Name-Indication`: https://en.wikipedia.org/wiki/Server_Name_Indication
|
||||
.. _`virtual hosting`: https://en.wikipedia.org/wiki/Virtual_hosting
|
||||
|
||||
@@ -1,10 +1,66 @@
|
||||
Integrations
|
||||
============
|
||||
Modules
|
||||
=======
|
||||
|
||||
- `requests-oauth <https://github.com/maraujop/requests-oauth>`_, adds OAuth support to Requests.
|
||||
- `FacePy <https://github.com/jgorset/facepy>`_, a Python wrapper to the Facebook API.
|
||||
- `robotframework-requests <https://github.com/bulkan/robotframework-requests>`_, a Robot Framework API wrapper.
|
||||
- `fullerene <https://github.com/bitprophet/fullerene>`_, a Graphite Dashboard.
|
||||
- `urbanairship-python <https://github.com/benjaminws/urbanairship-python>`_, a fork of the Urban Airship API wrapper.
|
||||
- `WhitespaceBot <https://github.com/Gunio/WhitespaceBot/>`_, a project that automatically forks repos, strips trailing whitespace, and sends a pull request.
|
||||
- `python-rexster <https://github.com/CulturePlex/python-rexster>`_, Rexter client that provides a simple interface for graph databases.
|
||||
- `daikon <https://github.com/neogenix/daikon>`_, a CLI for ElasticSearch.
|
||||
|
||||
Articles & Talks
|
||||
================
|
||||
- `Daniel Greenfeld's Review of Requests <https://pydanny.blogspot.com/2011/05/python-http-requests-for-humans.html>`_
|
||||
- `Issac Kelly's 'Consuming Web APIs' talk <https://issackelly.github.io/Consuming-Web-APIs-with-Python-Talk/slides/slides.html>`_
|
||||
- `Blog post about Requests via Yum <https://arunsag.wordpress.com/2011/08/17/new-package-python-requests-http-for-humans/>`_
|
||||
- `Russian blog post introducing Requests <https://habr.com/post/126262/>`_
|
||||
- `Sending JSON in Requests <https://www.coglib.com/~icordasc/blog/2014/11/sending-json-in-requests.html>`_
|
||||
- `Python for the Web <http://gun.io/blog/python-for-the-web/>`_ teaches how to use Python to interact with the web, using Requests.
|
||||
- `Daniel Greenfield's Review of Requests <http://pydanny.blogspot.com/2011/05/python-http-requests-for-humans.html>`_
|
||||
- `My 'Python for Humans' talk <http://python-for-humans.heroku.com>`_ ( `audio <http://codeconf.s3.amazonaws.com/2011/pycodeconf/talks/PyCodeConf2011%20-%20Kenneth%20Reitz.m4a>`_ )
|
||||
- `Issac Kelly's 'Consuming Web APIs' talk <http://issackelly.github.com/Consuming-Web-APIs-with-Python-Talk/slides/slides.html>`_
|
||||
- `Blog post about Requests via Yum <http://arunsag.wordpress.com/2011/08/17/new-package-python-requests-http-for-humans/>`_
|
||||
- `Russian blog post introducing Requests <http://habrahabr.ru/blogs/python/126262/>`_
|
||||
- `French blog post introducing Requests <http://www.nicosphere.net/requests-urllib2-de-python-simplifie-2432/>`_
|
||||
|
||||
|
||||
Integrations
|
||||
============
|
||||
|
||||
ScraperWiki
|
||||
------------
|
||||
|
||||
`ScraperWiki <https://scraperwiki.com/>`_ is an excellent service that allows
|
||||
you to run Python, Ruby, and PHP scraper scripts on the web. Now, Requests
|
||||
v0.6.1 is available to use in your scrapers!
|
||||
|
||||
To give it a try, simply::
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
Managed Packages
|
||||
================
|
||||
|
||||
Requests is available in a number of popular package formats. Of course,
|
||||
the ideal way to install Requests is via The Cheeseshop.
|
||||
|
||||
|
||||
Ubuntu & Debian
|
||||
---------------
|
||||
|
||||
Requests is available installed as a Debian package! Debian Etch Ubuntu, since Oneiric::
|
||||
|
||||
$ apt-get install python-requests
|
||||
|
||||
Unfortunately, the most recent version available is v0.5.0. If you're on the
|
||||
Debian Python Package team, I'd love an update of that :)
|
||||
|
||||
|
||||
Fedora and RedHat
|
||||
-----------------
|
||||
|
||||
You can easily install Requests v0.6.1 with yum on rpm-based systems::
|
||||
|
||||
$ yum install python-requests
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
.. _recommended:
|
||||
|
||||
Recommended Packages and Extensions
|
||||
===================================
|
||||
|
||||
Requests has a great variety of powerful and useful third-party extensions.
|
||||
This page provides an overview of some of the best of them.
|
||||
|
||||
Certifi CA Bundle
|
||||
-----------------
|
||||
|
||||
`Certifi`_ is a carefully curated collection of Root Certificates for
|
||||
validating the trustworthiness of SSL certificates while verifying the
|
||||
identity of TLS hosts. It has been extracted from the Requests project.
|
||||
|
||||
.. _Certifi: https://github.com/certifi/python-certifi
|
||||
|
||||
CacheControl
|
||||
------------
|
||||
|
||||
`CacheControl`_ is an extension that adds a full HTTP cache to Requests. This
|
||||
makes your web requests substantially more efficient, and should be used
|
||||
whenever you're making a lot of web requests.
|
||||
|
||||
.. _CacheControl: https://cachecontrol.readthedocs.io/en/latest/
|
||||
|
||||
Requests-Toolbelt
|
||||
-----------------
|
||||
|
||||
`Requests-Toolbelt`_ is a collection of utilities that some users of Requests may desire,
|
||||
but do not belong in Requests proper. This library is actively maintained
|
||||
by members of the Requests core team, and reflects the functionality most
|
||||
requested by users within the community.
|
||||
|
||||
.. _Requests-Toolbelt: https://toolbelt.readthedocs.io/en/latest/index.html
|
||||
|
||||
|
||||
Requests-Threads
|
||||
----------------
|
||||
|
||||
`Requests-Threads`_ is a Requests session that returns the amazing Twisted's awaitable Deferreds instead of Response objects. This allows the use of ``async``/``await`` keyword usage on Python 3, or Twisted's style of programming, if desired.
|
||||
|
||||
.. _Requests-Threads: https://github.com/requests/requests-threads
|
||||
|
||||
Requests-OAuthlib
|
||||
-----------------
|
||||
|
||||
`requests-oauthlib`_ makes it possible to do the OAuth dance from Requests
|
||||
automatically. This is useful for the large number of websites that use OAuth
|
||||
to provide authentication. It also provides a lot of tweaks that handle ways
|
||||
that specific OAuth providers differ from the standard specifications.
|
||||
|
||||
.. _requests-oauthlib: https://requests-oauthlib.readthedocs.io/en/latest/
|
||||
|
||||
|
||||
Betamax
|
||||
-------
|
||||
|
||||
`Betamax`_ records your HTTP interactions so the NSA does not have to.
|
||||
A VCR imitation designed only for Python-Requests.
|
||||
|
||||
.. _betamax: https://github.com/betamaxpy/betamax
|
||||
@@ -1,53 +0,0 @@
|
||||
Release Process and Rules
|
||||
=========================
|
||||
|
||||
.. versionadded:: v2.6.2
|
||||
|
||||
Starting with the version to be released after ``v2.6.2``, the following rules
|
||||
will govern and describe how the Requests core team produces a new release.
|
||||
|
||||
Major Releases
|
||||
--------------
|
||||
|
||||
A major release will include breaking changes. When it is versioned, it will
|
||||
be versioned as ``vX.0.0``. For example, if the previous release was
|
||||
``v10.2.7`` the next version will be ``v11.0.0``.
|
||||
|
||||
Breaking changes are changes that break backwards compatibility with prior
|
||||
versions. If the project were to change the ``text`` attribute on a
|
||||
``Response`` object to a method, that would only happen in a Major release.
|
||||
|
||||
Major releases may also include miscellaneous bug fixes. The core developers of
|
||||
Requests are committed to providing a good user experience. This means we're
|
||||
also committed to preserving backwards compatibility as much as possible. Major
|
||||
releases will be infrequent and will need strong justifications before they are
|
||||
considered.
|
||||
|
||||
Minor Releases
|
||||
--------------
|
||||
|
||||
A minor release will not include breaking changes but may include miscellaneous
|
||||
bug fixes. If the previous version of Requests released was ``v10.2.7`` a minor
|
||||
release would be versioned as ``v10.3.0``.
|
||||
|
||||
Minor releases will be backwards compatible with releases that have the same
|
||||
major version number. In other words, all versions that would start with
|
||||
``v10.`` should be compatible with each other.
|
||||
|
||||
Hotfix Releases
|
||||
---------------
|
||||
|
||||
A hotfix release will only include bug fixes that were missed when the project
|
||||
released the previous version. If the previous version of Requests released
|
||||
``v10.2.7`` the hotfix release would be versioned as ``v10.2.8``.
|
||||
|
||||
Hotfixes will **not** include upgrades to vendored dependencies after
|
||||
``v2.6.2``
|
||||
|
||||
Reasoning
|
||||
---------
|
||||
|
||||
In the 2.5 and 2.6 release series, the Requests core team upgraded vendored
|
||||
dependencies and caused a great deal of headaches for both users and the core
|
||||
team. To reduce this pain, we're forming a concrete set of procedures so
|
||||
expectations will be properly set.
|
||||
@@ -3,29 +3,35 @@
|
||||
Support
|
||||
=======
|
||||
|
||||
If you have questions or issues about Requests, there are several options:
|
||||
If you have a questions or issues about Requests, there are several options:
|
||||
|
||||
Stack Overflow
|
||||
--------------
|
||||
Send a Tweet
|
||||
------------
|
||||
|
||||
If your question does not contain sensitive (possibly proprietary)
|
||||
information or can be properly anonymized, please ask a question on
|
||||
`Stack Overflow <https://stackoverflow.com/questions/tagged/python-requests>`_
|
||||
and use the tag ``python-requests``.
|
||||
If your question is less than 140 characters, feel free to send a tweet to
|
||||
`@kennethreitz <http://twitter.com/kennethreitz>`_.
|
||||
|
||||
|
||||
File an Issue
|
||||
-------------
|
||||
|
||||
If you notice some unexpected behaviour in Requests, or want to see support
|
||||
If you notice some unexpected behavior in Requests, or want to see support
|
||||
for a new feature,
|
||||
`file an issue on GitHub <https://github.com/psf/requests/issues>`_.
|
||||
`file an issue on GitHub <https://github.com/kennethreitz/requests/issues>`_.
|
||||
|
||||
|
||||
Send a Tweet
|
||||
------------
|
||||
E-mail
|
||||
------
|
||||
|
||||
If your question is less than 280 characters, feel free to send a tweet to
|
||||
`@nateprewitt <https://twitter.com/nateprewitt>`_,
|
||||
`@sethmlarson <https://twitter.com/sethmlarson>`_, or
|
||||
`@sigmavirus24 <https://twitter.com/sigmavirus24>`_.
|
||||
I'm more than happy to answer any personal or in-depth questions about
|
||||
Requests. Feel free to email
|
||||
`requests@kennethreitz.com <mailto:requests@kennethreitz.com>`_.
|
||||
|
||||
|
||||
IRC
|
||||
---
|
||||
|
||||
The official Freenode channel for Requests is
|
||||
`#python-requests <irc://irc.freenode.net/python-requests>`_
|
||||
|
||||
I'm also available as **kennethreitz** on Freenode.
|
||||
@@ -1,18 +1,30 @@
|
||||
.. _updates:
|
||||
|
||||
|
||||
Community Updates
|
||||
=================
|
||||
Updates
|
||||
=======
|
||||
|
||||
If you'd like to stay up to date on the community and development of Requests,
|
||||
there are several options:
|
||||
|
||||
|
||||
GitHub
|
||||
------
|
||||
|
||||
The best way to track the development of Requests is through
|
||||
`the GitHub repo <https://github.com/psf/requests>`_.
|
||||
`the GitHub repo <https://github.com/kennethreitz/requests>`_.
|
||||
|
||||
Twitter
|
||||
-------
|
||||
|
||||
I often tweet about new features and releases of Requests.
|
||||
|
||||
Follow `@kennethreitz <https://twitter.com/kennethreitz>`_ for updates.
|
||||
|
||||
|
||||
Mailing List
|
||||
------------
|
||||
|
||||
There's a low-volume mailing list for Requests. To subscribe to the
|
||||
mailing list, send an email to
|
||||
`requests@librelist.org <mailto:requests@librelist.org>`_.
|
||||
|
||||
|
||||
.. include:: ../../HISTORY.md
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
Vulnerability Disclosure
|
||||
========================
|
||||
|
||||
The latest vulnerability disclosure information can be found on GitHub in our
|
||||
`Security Policy <https://github.com/psf/requests/blob/main/.github/SECURITY.md>`_.
|
||||
313
docs/conf.py
@@ -1,10 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Requests documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri Feb 19 00:05:47 2016.
|
||||
# sphinx-quickstart on Sun Feb 13 23:54:25 2011.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
@@ -12,188 +11,148 @@
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# Insert Requests' path into the system.
|
||||
sys.path.insert(0, os.path.abspath(".."))
|
||||
sys.path.insert(0, os.path.abspath("_themes"))
|
||||
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
import requests
|
||||
from requests import __version__
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.0'
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.intersphinx",
|
||||
"sphinx.ext.todo",
|
||||
"sphinx.ext.viewcode",
|
||||
]
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.autodoc']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ["_templates"]
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = ".rst"
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
root_doc = "index"
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u"Requests"
|
||||
copyright = u"Kenneth Reitz and contributors"
|
||||
author = u"Kenneth Reitz"
|
||||
project = u'Requests'
|
||||
copyright = u'2012. A <a href="http://kennethreitz.com/pages/open-projects.html">Kenneth Reitz</a> Project'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = requests.__version__
|
||||
version = __version__
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = requests.__version__
|
||||
release = version
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
# today = ''
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
# today_fmt = '%B %d, %Y'
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ["_build"]
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
# default_role = None
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = False
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
# show_authors = False
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = "flask_theme_support.FlaskyStyle"
|
||||
pygments_style = 'flask_theme_support.FlaskyStyle'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
# keep_warnings = False
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = "alabaster"
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {
|
||||
"show_powered_by": False,
|
||||
"github_user": "psf",
|
||||
"github_repo": "requests",
|
||||
"github_banner": True,
|
||||
"show_related": False,
|
||||
"note_bg": "#FFF59C",
|
||||
}
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
#html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
# html_title = None
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
# html_short_title = None
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
# html_logo = None
|
||||
#html_logo = None
|
||||
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
# html_favicon = None
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ["_static"]
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
# html_extra_path = []
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
html_use_smartypants = False
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {
|
||||
"index": ["sidebar.html", "sourcelink.html", "searchbox.html"],
|
||||
"**": [
|
||||
"sidebar.html",
|
||||
"localtoc.html",
|
||||
"relations.html",
|
||||
"sourcelink.html",
|
||||
"searchbox.html",
|
||||
],
|
||||
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
|
||||
'**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
|
||||
'sourcelink.html', 'searchbox.html']
|
||||
}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
# html_additional_pages = {}
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
# html_domain_indices = True
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
# html_use_index = True
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
# html_split_index = False
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
html_show_sourcelink = False
|
||||
@@ -202,184 +161,84 @@ html_show_sourcelink = False
|
||||
html_show_sphinx = False
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
html_show_copyright = True
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
# html_use_opensearch = ''
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
|
||||
# Language to be used for generating the HTML full-text search index.
|
||||
# Sphinx supports the following languages:
|
||||
# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
|
||||
# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
|
||||
# html_search_language = 'en'
|
||||
|
||||
# A dictionary with options for the search language support, empty by default.
|
||||
# Now only 'ja' uses this config value
|
||||
# html_search_options = {'type': 'default'}
|
||||
|
||||
# The name of a javascript file (relative to the configuration directory) that
|
||||
# implements a search results scorer. If empty, the default will be used.
|
||||
# html_search_scorer = 'scorer.js'
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = "Requestsdoc"
|
||||
htmlhelp_basename = 'Requestsdoc'
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
# Latex figure (float) alignment
|
||||
#'figure_align': 'htbp',
|
||||
}
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
(root_doc, "Requests.tex", u"Requests Documentation", u"Kenneth Reitz", "manual")
|
||||
('index', 'Requests.tex', u'Requests Documentation',
|
||||
u'Kenneth Reitz', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
# latex_logo = None
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
# latex_use_parts = False
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
# latex_show_pagerefs = False
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# latex_show_urls = False
|
||||
#latex_show_urls = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# latex_appendices = []
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# latex_domain_indices = True
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [(root_doc, "requests", u"Requests Documentation", [author], 1)]
|
||||
man_pages = [
|
||||
('index', 'requests', u'Requests Documentation',
|
||||
[u'Kenneth Reitz'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
||||
#man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
# -- Options for Texinfo output ------------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(
|
||||
root_doc,
|
||||
"Requests",
|
||||
u"Requests Documentation",
|
||||
author,
|
||||
"Requests",
|
||||
"One line description of project.",
|
||||
"Miscellaneous",
|
||||
)
|
||||
('index', 'Requests', u'Requests Documentation', u'Kenneth Reitz',
|
||||
'Requests', 'One line description of project.', 'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# texinfo_appendices = []
|
||||
texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
|
||||
# -- Options for Epub output ----------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
epub_title = project
|
||||
epub_author = author
|
||||
epub_publisher = author
|
||||
epub_copyright = copyright
|
||||
|
||||
# The basename for the epub file. It defaults to the project name.
|
||||
# epub_basename = project
|
||||
|
||||
# The HTML theme for the epub output. Since the default themes are not
|
||||
# optimized for small screen space, using the same theme for HTML and epub
|
||||
# output is usually not wise. This defaults to 'epub', a theme designed to save
|
||||
# visual space.
|
||||
# epub_theme = 'epub'
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or 'en' if the language is not set.
|
||||
# epub_language = ''
|
||||
|
||||
# The scheme of the identifier. Typical schemes are ISBN or URL.
|
||||
# epub_scheme = ''
|
||||
|
||||
# The unique identifier of the text. This can be a ISBN number
|
||||
# or the project homepage.
|
||||
# epub_identifier = ''
|
||||
|
||||
# A unique identification for the text.
|
||||
# epub_uid = ''
|
||||
|
||||
# A tuple containing the cover image and cover page html template filenames.
|
||||
# epub_cover = ()
|
||||
|
||||
# A sequence of (type, uri, title) tuples for the guide element of content.opf.
|
||||
# epub_guide = ()
|
||||
|
||||
# HTML files that should be inserted before the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
# epub_pre_files = []
|
||||
|
||||
# HTML files that should be inserted after the pages created by sphinx.
|
||||
# The format is a list of tuples containing the path and title.
|
||||
# epub_post_files = []
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
epub_exclude_files = ["search.html"]
|
||||
|
||||
# The depth of the table of contents in toc.ncx.
|
||||
# epub_tocdepth = 3
|
||||
|
||||
# Allow duplicate toc entries.
|
||||
# epub_tocdup = True
|
||||
|
||||
# Choose between 'default' and 'includehidden'.
|
||||
# epub_tocscope = 'default'
|
||||
|
||||
# Fix unsupported image types using the Pillow.
|
||||
# epub_fix_images = False
|
||||
|
||||
# Scale large images.
|
||||
# epub_max_image_width = 0
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# epub_show_urls = 'inline'
|
||||
|
||||
# If false, no index is generated.
|
||||
# epub_use_index = True
|
||||
|
||||
intersphinx_mapping = {
|
||||
"python": ("https://docs.python.org/3/", None),
|
||||
"urllib3": ("https://urllib3.readthedocs.io/en/latest", None),
|
||||
}
|
||||
sys.path.append(os.path.abspath('_themes'))
|
||||
html_theme_path = ['_themes']
|
||||
html_theme = 'kr'
|
||||
@@ -1,4 +1,5 @@
|
||||
Authors
|
||||
=======
|
||||
|
||||
.. include:: ../../AUTHORS.rst
|
||||
|
||||
.. include:: ../../AUTHORS.rst
|
||||
@@ -1,165 +0,0 @@
|
||||
.. _contributing:
|
||||
|
||||
Contributor's Guide
|
||||
===================
|
||||
|
||||
If you're reading this, you're probably interested in contributing to Requests.
|
||||
Thank you very much! Open source projects live-and-die based on the support
|
||||
they receive from others, and the fact that you're even considering
|
||||
contributing to the Requests project is *very* generous of you.
|
||||
|
||||
This document lays out guidelines and advice for contributing to this project.
|
||||
If you're thinking of contributing, please start by reading this document and
|
||||
getting a feel for how contributing to this project works. If you have any
|
||||
questions, feel free to reach out to either `Nate Prewitt`_, `Ian Cordasco`_,
|
||||
or `Seth Michael Larson`_, the primary maintainers.
|
||||
|
||||
.. _Ian Cordasco: https://www.coglib.com/~icordasc/
|
||||
.. _Nate Prewitt: https://www.nateprewitt.com/
|
||||
.. _Seth Michael Larson: https://sethmlarson.dev/
|
||||
|
||||
The guide is split into sections based on the type of contribution you're
|
||||
thinking of making, with a section that covers general guidelines for all
|
||||
contributors.
|
||||
|
||||
Code of Conduct
|
||||
---------------
|
||||
|
||||
The Python community is made up of members from around the globe with a diverse
|
||||
set of skills, personalities, and experiences. It is through these differences
|
||||
that our community experiences great successes and continued growth. When you're
|
||||
working with members of the community, follow the
|
||||
`Python Software Foundation Code of Conduct`_ to help steer your interactions
|
||||
and keep Python a positive, successful, and growing community.
|
||||
|
||||
.. _Python Software Foundation Code of Conduct: https://policies.python.org/python.org/code-of-conduct/
|
||||
|
||||
.. _early-feedback:
|
||||
|
||||
Get Early Feedback
|
||||
------------------
|
||||
|
||||
If you are contributing, do not feel the need to sit on your contribution until
|
||||
it is perfectly polished and complete. It helps everyone involved for you to
|
||||
seek feedback as early as you possibly can. Submitting an early, unfinished
|
||||
version of your contribution for feedback in no way prejudices your chances of
|
||||
getting that contribution accepted, and can save you from putting a lot of work
|
||||
into a contribution that is not suitable for the project.
|
||||
|
||||
Contribution Suitability
|
||||
------------------------
|
||||
|
||||
Our project maintainers have the last word on whether or not a contribution is
|
||||
suitable for Requests. All contributions will be considered carefully, but from
|
||||
time to time, contributions will be rejected because they do not suit the
|
||||
current goals or needs of the project.
|
||||
|
||||
If your contribution is rejected, don't despair! As long as you followed these
|
||||
guidelines, you will have a much better chance of getting your next
|
||||
contribution accepted.
|
||||
|
||||
|
||||
Code Contributions
|
||||
------------------
|
||||
|
||||
Steps for Submitting Code
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When contributing code, you'll want to follow this checklist:
|
||||
|
||||
1. Fork the repository on GitHub.
|
||||
2. Run the tests to confirm they all pass on your system. If they don't, you'll
|
||||
need to investigate why they fail. If you're unable to diagnose this
|
||||
yourself, raise it as a bug report by following the guidelines in this
|
||||
document: :ref:`bug-reports`.
|
||||
3. Write tests that demonstrate your bug or feature. Ensure that they fail.
|
||||
4. Make your change.
|
||||
5. Run the entire test suite again, confirming that all tests pass *including
|
||||
the ones you just added*.
|
||||
6. Send a GitHub Pull Request to the main repository's ``main`` branch.
|
||||
GitHub Pull Requests are the expected method of code collaboration on this
|
||||
project.
|
||||
|
||||
The following sub-sections go into more detail on some of the points above.
|
||||
|
||||
Code Review
|
||||
~~~~~~~~~~~
|
||||
|
||||
Contributions will not be merged until they've been code reviewed. You should
|
||||
implement any code review feedback unless you strongly object to it. In the
|
||||
event that you object to the code review feedback, you should make your case
|
||||
clearly and calmly. If, after doing so, the feedback is judged to still apply,
|
||||
you must either apply the feedback or withdraw your contribution.
|
||||
|
||||
Code Style
|
||||
~~~~~~~~~~
|
||||
|
||||
Requests uses a collection of tools to ensure the code base has a consistent
|
||||
style as it grows. We have these orchestrated using a tool called
|
||||
`pre-commit`_. This can be installed locally and run over your changes prior
|
||||
to opening a PR, and will also be run as part of the CI approval process
|
||||
before a change is merged.
|
||||
|
||||
You can find the full list of formatting requirements specified in the
|
||||
`.pre-commit-config.yaml`_ at the top level directory of Requests.
|
||||
|
||||
.. _pre-commit: https://pre-commit.com/
|
||||
.. _.pre-commit-config.yaml: https://github.com/psf/requests/blob/main/.pre-commit-config.yaml
|
||||
|
||||
New Contributors
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
If you are new or relatively new to Open Source, welcome! Requests aims to
|
||||
be a gentle introduction to the world of Open Source. If you're concerned about
|
||||
how best to contribute, please consider mailing a maintainer (listed above) and
|
||||
asking for help.
|
||||
|
||||
Please also check the :ref:`early-feedback` section.
|
||||
|
||||
|
||||
Documentation Contributions
|
||||
---------------------------
|
||||
|
||||
Documentation improvements are always welcome! The documentation files live in
|
||||
the ``docs/`` directory of the codebase. They're written in
|
||||
`reStructuredText`_, and use `Sphinx`_ to generate the full suite of
|
||||
documentation.
|
||||
|
||||
When contributing documentation, please do your best to follow the style of the
|
||||
documentation files. This means a soft-limit of 79 characters wide in your text
|
||||
files and a semi-formal, yet friendly and approachable, prose style.
|
||||
|
||||
When presenting Python code, use single-quoted strings (``'hello'`` instead of
|
||||
``"hello"``).
|
||||
|
||||
.. _reStructuredText: https://docutils.sourceforge.net/rst.html
|
||||
.. _Sphinx: https://www.sphinx-doc.org/en/master/
|
||||
|
||||
|
||||
.. _bug-reports:
|
||||
|
||||
Bug Reports
|
||||
-----------
|
||||
|
||||
Bug reports are hugely important! Before you raise one, though, please check
|
||||
through the `GitHub issues`_, **both open and closed**, to confirm that the bug
|
||||
hasn't been reported before. Duplicate bug reports are a huge drain on the time
|
||||
of other contributors, and should be avoided as much as possible.
|
||||
|
||||
.. _GitHub issues: https://github.com/psf/requests/issues
|
||||
|
||||
|
||||
Feature Requests
|
||||
----------------
|
||||
|
||||
Requests is in a perpetual feature freeze, only the BDFL can add or approve of
|
||||
new features. The maintainers believe that Requests is a feature-complete
|
||||
piece of software at this time.
|
||||
|
||||
One of the most important skills to have while maintaining a largely-used
|
||||
open source project is learning the ability to say "no" to suggested changes,
|
||||
while keeping an open ear and mind.
|
||||
|
||||
If you believe there is a feature missing, feel free to raise a feature
|
||||
request, but please do be aware that the overwhelming likelihood is that your
|
||||
feature request will not be accepted.
|
||||
12
docs/dev/todo.rst
Normal file
@@ -0,0 +1,12 @@
|
||||
How to Help
|
||||
===========
|
||||
|
||||
Requests is under active development, and contributions are more than welcome!
|
||||
|
||||
|
||||
What Needs to be Done
|
||||
---------------------
|
||||
|
||||
- HTTP Cert Checking (`#30 <https://github.com/kennethreitz/requests/issues/30>`_)
|
||||
- Python 3.x Support (`#70 <https://github.com/kennethreitz/requests/issues/70>`_)
|
||||
- Kerberos Support (`#47 <https://github.com/kennethreitz/requests/issues/47>`_)
|
||||
134
docs/index.rst
@@ -3,84 +3,79 @@
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Requests: HTTP for Humans™
|
||||
==========================
|
||||
Requests: HTTP for Humans
|
||||
=========================
|
||||
|
||||
Release v\ |version|. (:ref:`Installation <install>`)
|
||||
|
||||
Requests is an :ref:`ISC Licensed <isc>` HTTP library, written in Python, for human beings.
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/requests.svg?maxAge=86400
|
||||
:target: https://pypi.org/project/requests/
|
||||
:alt: PyPI Version Badge
|
||||
Python's standard **urllib2** module provides most of
|
||||
the HTTP capabilities you need, but the API is thoroughly **broken**.
|
||||
It was built for a different time — and a different web. It requires an *enormous* amount of work (even method overrides) to perform the simplest of tasks.
|
||||
|
||||
.. image:: https://img.shields.io/pypi/pyversions/requests.svg
|
||||
:target: https://pypi.org/project/requests/
|
||||
:alt: Supported Versions Badge
|
||||
Things shouldn’t be this way. Not in Python.
|
||||
|
||||
.. image:: https://static.pepy.tech/badge/requests/month
|
||||
:target: https://pepy.tech/project/requests
|
||||
:alt: Downloads Per Month Badge
|
||||
::
|
||||
|
||||
.. image:: https://img.shields.io/github/contributors/psf/requests.svg
|
||||
:target: https://github.com/psf/requests/graphs/contributors
|
||||
:alt: Contributors Badge
|
||||
|
||||
.. image:: https://readthedocs.org/projects/requests/badge/?version=latest
|
||||
:target: https://requests.readthedocs.io
|
||||
:alt: Documentation Badge
|
||||
|
||||
**Requests** is an elegant and simple HTTP library for Python, built for human beings.
|
||||
|
||||
-------------------
|
||||
|
||||
**Behold, the power of Requests**::
|
||||
|
||||
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
|
||||
>>> r = requests.get('https://api.github.com', auth=('user', 'pass'))
|
||||
>>> r.status_code
|
||||
200
|
||||
204
|
||||
>>> r.headers['content-type']
|
||||
'application/json; charset=utf8'
|
||||
>>> r.encoding
|
||||
'utf-8'
|
||||
>>> r.text
|
||||
'{"type":"User"...'
|
||||
>>> r.json()
|
||||
{'private_gists': 419, 'total_private_repos': 77, ...}
|
||||
'application/json'
|
||||
>>> r.content
|
||||
...
|
||||
|
||||
See `similar code, sans Requests <https://gist.github.com/973705>`_.
|
||||
See `the same code, without Requests <https://gist.github.com/973705>`_.
|
||||
|
||||
Requests takes all of the work out of Python HTTP — making your integration with web services seamless. There's no need to manually add query strings to your URLs, or to form-encode your POST data. Keep-alive and HTTP connection pooling are 100% automatic, powered by `urllib3 <https://github.com/shazow/urllib3>`_, which is embedded within Requests.
|
||||
|
||||
|
||||
**Requests** allows you to send HTTP/1.1 requests extremely easily.
|
||||
There's no need to manually add query strings to your
|
||||
URLs, or to form-encode your POST data. Keep-alive and HTTP connection pooling
|
||||
are 100% automatic, thanks to `urllib3 <https://github.com/urllib3/urllib3>`_.
|
||||
Testimonials
|
||||
------------
|
||||
|
||||
Beloved Features
|
||||
----------------
|
||||
`The Washington Post <http://www.washingtonpost.com/>`_,
|
||||
`Twitter, Inc <http://twitter.com>`_,
|
||||
`Readability <http://readability.com>`_, and
|
||||
Federal US Institutions
|
||||
use Requests internally. It has been installed over 60,000 times from PyPI.
|
||||
|
||||
**Armin Ronacher**
|
||||
Requests is the perfect example how beautiful an API can be with the
|
||||
right level of abstraction.
|
||||
|
||||
**Matt DeBoard**
|
||||
I'm going to get @kennethreitz's Python requests module tattooed
|
||||
on my body, somehow. The whole thing.
|
||||
|
||||
**Daniel Greenfeld**
|
||||
Nuked a 1200 LOC spaghetti code library with 10 lines of code thanks to
|
||||
@kennethreitz's request library. Today has been AWESOME.
|
||||
|
||||
**Kenny Meyers**
|
||||
Python HTTP: When in doubt, or when not in doubt, use Requests. Beautiful,
|
||||
simple, Pythonic.
|
||||
|
||||
|
||||
Feature Support
|
||||
---------------
|
||||
|
||||
Requests is ready for today's web.
|
||||
|
||||
- Keep-Alive & Connection Pooling
|
||||
- International Domains and URLs
|
||||
- Keep-Alive & Connection Pooling
|
||||
- Sessions with Cookie Persistence
|
||||
- Browser-style SSL Verification
|
||||
- Automatic Content Decoding
|
||||
- Basic/Digest Authentication
|
||||
- Elegant Key/Value Cookies
|
||||
- Automatic Decompression
|
||||
- Unicode Response Bodies
|
||||
- HTTP(S) Proxy Support
|
||||
- Multipart File Uploads
|
||||
- Streaming Downloads
|
||||
- Connection Timeouts
|
||||
- Chunked Requests
|
||||
- ``.netrc`` Support
|
||||
|
||||
Requests officially supports Python 3.10+, and runs great on PyPy.
|
||||
|
||||
|
||||
The User Guide
|
||||
--------------
|
||||
User Guide
|
||||
----------
|
||||
|
||||
This part of the documentation, which is mostly prose, begins with some
|
||||
background information about Requests, then focuses on step-by-step
|
||||
@@ -89,37 +84,30 @@ instructions for getting the most out of Requests.
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
user/intro
|
||||
user/install
|
||||
user/quickstart
|
||||
user/advanced
|
||||
user/authentication
|
||||
|
||||
|
||||
The Community Guide
|
||||
-------------------
|
||||
Community Guide
|
||||
-----------------
|
||||
|
||||
This part of the documentation, which is mostly prose, details the
|
||||
Requests ecosystem and community.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
community/recommended
|
||||
community/faq
|
||||
community/out-there
|
||||
community/support
|
||||
community/vulnerabilities
|
||||
community/release-process
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
community/faq
|
||||
community/out-there.rst
|
||||
community/support
|
||||
community/updates
|
||||
|
||||
The API Documentation / Guide
|
||||
-----------------------------
|
||||
API Documentation
|
||||
-----------------
|
||||
|
||||
If you are looking for information on a specific function, class, or method,
|
||||
If you are looking for information on a specific function, class or method,
|
||||
this part of the documentation is for you.
|
||||
|
||||
.. toctree::
|
||||
@@ -128,17 +116,15 @@ this part of the documentation is for you.
|
||||
api
|
||||
|
||||
|
||||
The Contributor Guide
|
||||
---------------------
|
||||
Developer Guide
|
||||
---------------
|
||||
|
||||
If you want to contribute to the project, this part of the documentation is for
|
||||
you.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:maxdepth: 1
|
||||
|
||||
dev/contributing
|
||||
dev/internals
|
||||
dev/todo
|
||||
dev/authors
|
||||
|
||||
There are no more guides. You are now guideless.
|
||||
Good luck.
|
||||
|
||||
453
docs/make.bat
@@ -1,263 +1,190 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. xml to make Docutils-native XML files
|
||||
echo. pseudoxml to make pseudoxml-XML files for display purposes
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
echo. coverage to run coverage check of the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
|
||||
REM Check if sphinx-build is available and fallback to Python version if any
|
||||
%SPHINXBUILD% 1>NUL 2>NUL
|
||||
if errorlevel 9009 goto sphinx_python
|
||||
goto sphinx_ok
|
||||
|
||||
:sphinx_python
|
||||
|
||||
set SPHINXBUILD=python -m sphinx.__init__
|
||||
%SPHINXBUILD% 2> nul
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:sphinx_ok
|
||||
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Requests.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Requests.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdf" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf
|
||||
cd %~dp0
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdfja" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf-ja
|
||||
cd %~dp0
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "coverage" (
|
||||
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of coverage in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/coverage/python.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "xml" (
|
||||
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The XML files are in %BUILDDIR%/xml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pseudoxml" (
|
||||
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Requests.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Requests.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Pinning to avoid unexpected breakages.
|
||||
# Used by RTD to generate docs.
|
||||
Sphinx==7.2.6
|
||||
@@ -1,155 +0,0 @@
|
||||
.. _authentication:
|
||||
|
||||
Authentication
|
||||
==============
|
||||
|
||||
This document discusses using various kinds of authentication with Requests.
|
||||
|
||||
Many web services require authentication, and there are many different types.
|
||||
Below, we outline various forms of authentication available in Requests, from
|
||||
the simple to the complex.
|
||||
|
||||
|
||||
Basic Authentication
|
||||
--------------------
|
||||
|
||||
Many web services that require authentication accept HTTP Basic Auth. This is
|
||||
the simplest kind, and Requests supports it straight out of the box.
|
||||
|
||||
Making requests with HTTP Basic Auth is very simple::
|
||||
|
||||
>>> from requests.auth import HTTPBasicAuth
|
||||
>>> basic = HTTPBasicAuth('user', 'pass')
|
||||
>>> requests.get('https://httpbin.org/basic-auth/user/pass', auth=basic)
|
||||
<Response [200]>
|
||||
|
||||
In fact, HTTP Basic Auth is so common that Requests provides a handy shorthand
|
||||
for using it::
|
||||
|
||||
>>> requests.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass'))
|
||||
<Response [200]>
|
||||
|
||||
Providing the credentials in a tuple like this is exactly the same as the
|
||||
``HTTPBasicAuth`` example above.
|
||||
|
||||
|
||||
netrc Authentication
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If no authentication method is given with the ``auth`` argument, Requests will
|
||||
attempt to get the authentication credentials for the URL's hostname from the
|
||||
user's netrc file. The netrc file overrides raw HTTP authentication headers
|
||||
set with `headers=`.
|
||||
|
||||
If credentials for the hostname are found, the request is sent with HTTP Basic
|
||||
Auth.
|
||||
|
||||
Requests will search for the netrc file at `~/.netrc`, `~/_netrc`, or at the path
|
||||
specified by the `NETRC` environment variable. `~` denotes the user's home
|
||||
directory, which is `$HOME` on Unix based systems and `%USERPROFILE%` on Windows.
|
||||
|
||||
Usage of netrc file can be disabled by setting `trust_env` to `False` in the
|
||||
Requests session::
|
||||
|
||||
>>> s = requests.Session()
|
||||
>>> s.trust_env = False
|
||||
>>> s.get('https://httpbin.org/basic-auth/user/pass')
|
||||
|
||||
Digest Authentication
|
||||
---------------------
|
||||
|
||||
Another very popular form of HTTP Authentication is Digest Authentication,
|
||||
and Requests supports this out of the box as well::
|
||||
|
||||
>>> from requests.auth import HTTPDigestAuth
|
||||
>>> url = 'https://httpbin.org/digest-auth/auth/user/pass'
|
||||
>>> requests.get(url, auth=HTTPDigestAuth('user', 'pass'))
|
||||
<Response [200]>
|
||||
|
||||
|
||||
OAuth 1 Authentication
|
||||
----------------------
|
||||
|
||||
A common form of authentication for several web APIs is OAuth. The ``requests-oauthlib``
|
||||
library allows Requests users to easily make OAuth 1 authenticated requests::
|
||||
|
||||
>>> import requests
|
||||
>>> from requests_oauthlib import OAuth1
|
||||
|
||||
>>> url = 'https://api.twitter.com/1.1/account/verify_credentials.json'
|
||||
>>> auth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET',
|
||||
... 'USER_OAUTH_TOKEN', 'USER_OAUTH_TOKEN_SECRET')
|
||||
|
||||
>>> requests.get(url, auth=auth)
|
||||
<Response [200]>
|
||||
|
||||
For more information on how to OAuth flow works, please see the official `OAuth`_ website.
|
||||
For examples and documentation on requests-oauthlib, please see the `requests_oauthlib`_
|
||||
repository on GitHub
|
||||
|
||||
OAuth 2 and OpenID Connect Authentication
|
||||
-----------------------------------------
|
||||
|
||||
The ``requests-oauthlib`` library also handles OAuth 2, the authentication mechanism
|
||||
underpinning OpenID Connect. See the `requests-oauthlib OAuth2 documentation`_ for
|
||||
details of the various OAuth 2 credential management flows:
|
||||
|
||||
* `Web Application Flow`_
|
||||
* `Mobile Application Flow`_
|
||||
* `Legacy Application Flow`_
|
||||
* `Backend Application Flow`_
|
||||
|
||||
Other Authentication
|
||||
--------------------
|
||||
|
||||
Requests is designed to allow other forms of authentication to be easily and
|
||||
quickly plugged in. Members of the open-source community frequently write
|
||||
authentication handlers for more complicated or less commonly-used forms of
|
||||
authentication. Some of the best have been brought together under the
|
||||
`Requests organization`_, including:
|
||||
|
||||
- Kerberos_
|
||||
- NTLM_
|
||||
|
||||
If you want to use any of these forms of authentication, go straight to their
|
||||
GitHub page and follow the instructions.
|
||||
|
||||
|
||||
New Forms of Authentication
|
||||
---------------------------
|
||||
|
||||
If you can't find a good implementation of the form of authentication you
|
||||
want, you can implement it yourself. Requests makes it easy to add your own
|
||||
forms of authentication.
|
||||
|
||||
To do so, subclass :class:`AuthBase <requests.auth.AuthBase>` and implement the
|
||||
``__call__()`` method::
|
||||
|
||||
>>> import requests
|
||||
>>> class MyAuth(requests.auth.AuthBase):
|
||||
... def __call__(self, r):
|
||||
... # Implement my authentication
|
||||
... return r
|
||||
...
|
||||
>>> url = 'https://httpbin.org/get'
|
||||
>>> requests.get(url, auth=MyAuth())
|
||||
<Response [200]>
|
||||
|
||||
When an authentication handler is attached to a request,
|
||||
it is called during request setup. The ``__call__`` method must therefore do
|
||||
whatever is required to make the authentication work. Some forms of
|
||||
authentication will additionally add hooks to provide further functionality.
|
||||
|
||||
Further examples can be found under the `Requests organization`_ and in the
|
||||
``auth.py`` file.
|
||||
|
||||
.. _OAuth: https://oauth.net/
|
||||
.. _requests_oauthlib: https://github.com/requests/requests-oauthlib
|
||||
.. _requests-oauthlib OAuth2 documentation: https://requests-oauthlib.readthedocs.io/en/latest/oauth2_workflow.html
|
||||
.. _Web Application Flow: https://requests-oauthlib.readthedocs.io/en/latest/oauth2_workflow.html#web-application-flow
|
||||
.. _Mobile Application Flow: https://requests-oauthlib.readthedocs.io/en/latest/oauth2_workflow.html#mobile-application-flow
|
||||
.. _Legacy Application Flow: https://requests-oauthlib.readthedocs.io/en/latest/oauth2_workflow.html#legacy-application-flow
|
||||
.. _Backend Application Flow: https://requests-oauthlib.readthedocs.io/en/latest/oauth2_workflow.html#backend-application-flow
|
||||
.. _Kerberos: https://github.com/requests/requests-kerberos
|
||||
.. _NTLM: https://github.com/requests/requests-ntlm
|
||||
.. _Requests organization: https://github.com/requests
|
||||
@@ -1,36 +1,78 @@
|
||||
.. _install:
|
||||
|
||||
Installation of Requests
|
||||
========================
|
||||
Installation
|
||||
============
|
||||
|
||||
This part of the documentation covers the installation of Requests.
|
||||
The first step to using any software package is getting it properly installed.
|
||||
|
||||
|
||||
$ python -m pip install requests
|
||||
--------------------------------
|
||||
Distribute & Pip
|
||||
----------------
|
||||
|
||||
To install Requests, simply run this simple command in your terminal of choice::
|
||||
Installing requests is simple with `pip <http://www.pip-installer.org/>`_::
|
||||
|
||||
$ python -m pip install requests
|
||||
$ pip install requests
|
||||
|
||||
Get the Source Code
|
||||
-------------------
|
||||
or, with `easy_install <http://pypi.python.org/pypi/setuptools>`_::
|
||||
|
||||
$ easy_install requests
|
||||
|
||||
But, you really `shouldn't do that <http://www.pip-installer.org/en/latest/other-tools.html#pip-compared-to-easy-install>`_.
|
||||
|
||||
|
||||
|
||||
Cheeseshop Mirror
|
||||
-----------------
|
||||
|
||||
If the Cheeseshop is down, you can also install Requests from Kenneth Reitz's
|
||||
personal `Cheeseshop mirror <http://pip.kennethreitz.com/>`_::
|
||||
|
||||
$ pip install -i http://pip.kennethreitz.com/simple requests
|
||||
|
||||
|
||||
Get the Code
|
||||
------------
|
||||
|
||||
Requests is actively developed on GitHub, where the code is
|
||||
`always available <https://github.com/psf/requests>`_.
|
||||
`always available <https://github.com/kennethreitz/requests>`_.
|
||||
|
||||
You can either clone the public repository::
|
||||
|
||||
$ git clone https://github.com/psf/requests.git
|
||||
git clone git://github.com/kennethreitz/requests.git
|
||||
|
||||
Or, download the `tarball <https://github.com/psf/requests/tarball/main>`_::
|
||||
Download the `tarball <https://github.com/kennethreitz/requests/tarball/master>`_::
|
||||
|
||||
$ curl -OL https://github.com/psf/requests/tarball/main
|
||||
# optionally, zipball is also available (for Windows users).
|
||||
$ curl -O https://github.com/kennethreitz/requests/tarball/master
|
||||
|
||||
Once you have a copy of the source, you can embed it in your own Python
|
||||
package, or install it into your site-packages easily::
|
||||
Or, download the `zipball <https://github.com/kennethreitz/requests/zipball/master>`_::
|
||||
|
||||
$ cd requests
|
||||
$ python -m pip install .
|
||||
$ curl -O https://github.com/kennethreitz/requests/zipball/master
|
||||
|
||||
|
||||
Once you have a copy of the source, you can embed it in your Python package,
|
||||
or install it into your site-packages easily::
|
||||
|
||||
$ python setup.py install
|
||||
|
||||
.. _gevent:
|
||||
|
||||
Installing Gevent
|
||||
-----------------
|
||||
|
||||
If you are using the ``requests.async`` module for making concurrent
|
||||
requests, you need to install gevent.
|
||||
|
||||
To install gevent, you'll need ``libevent``.
|
||||
|
||||
OSX::
|
||||
|
||||
$ brew install libevent
|
||||
|
||||
Ubuntu::
|
||||
|
||||
$ apt-get install libevent-dev
|
||||
|
||||
Once you have ``libevent``, you can install ``gevent`` with ``pip``::
|
||||
|
||||
$ pip install gevent
|
||||
|
||||
49
docs/user/intro.rst
Normal file
@@ -0,0 +1,49 @@
|
||||
.. _introduction:
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
Philosophy
|
||||
----------
|
||||
|
||||
Requests was developed with a few :pep:`20` idioms in mind.
|
||||
|
||||
|
||||
#. Beautiful is better than ugly.
|
||||
#. Explicit is better than implicit.
|
||||
#. Simple is better than complex.
|
||||
#. Complex is better than complicated.
|
||||
#. Readability counts.
|
||||
|
||||
All contributions to Requests should keep these important rules in mind.
|
||||
|
||||
|
||||
ISC License
|
||||
-----------
|
||||
|
||||
A large number of open source projects you find today are `GPL Licensed`_.
|
||||
While the GPL has its time and place, it should most certainly not be your
|
||||
go-to license for your next open source project.
|
||||
|
||||
A project that is released as GPL cannot be used in any commercial product
|
||||
without the product itself also being offered as open source.
|
||||
|
||||
The MIT, BSD, ISC, and Apache2 licenses are great alternatives to the GPL
|
||||
that allow your open-source software to be used freely in proprietary,
|
||||
closed-source software.
|
||||
|
||||
Requests is released under terms of `The ISC License`_.
|
||||
|
||||
.. _`GPL Licensed`: http://www.opensource.org/licenses/gpl-license.php
|
||||
.. _`The ISC License`: http://www.opensource.org/licenses/isc-license
|
||||
|
||||
|
||||
Requests License
|
||||
----------------
|
||||
|
||||
Copyright (c) 2011, Kenneth Reitz
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
@@ -6,7 +6,8 @@ Quickstart
|
||||
.. module:: requests.models
|
||||
|
||||
Eager to get started? This page gives a good introduction in how to get started
|
||||
with Requests.
|
||||
with Requests. This assumes you already have Requests installed. If you do not,
|
||||
head over to the :ref:`Installation <install>` section.
|
||||
|
||||
First, make sure that:
|
||||
|
||||
@@ -14,194 +15,83 @@ First, make sure that:
|
||||
* Requests is :ref:`up-to-date <updates>`
|
||||
|
||||
|
||||
Let's get started with some simple examples.
|
||||
Lets gets started with some simple use cases and examples.
|
||||
|
||||
|
||||
Make a Request
|
||||
--------------
|
||||
Make a GET Request
|
||||
------------------
|
||||
|
||||
Making a request with Requests is very simple.
|
||||
Making a standard request with Requests is very simple.
|
||||
|
||||
Begin by importing the Requests module::
|
||||
Let's get GitHub's public timeline ::
|
||||
|
||||
>>> import requests
|
||||
r = requests.get('https://github.com/timeline.json')
|
||||
|
||||
Now, let's try to get a webpage. For this example, let's get GitHub's public
|
||||
timeline::
|
||||
Now, we have a :class:`Response` object called ``r``. We can get all the
|
||||
information we need from this.
|
||||
|
||||
>>> r = requests.get('https://api.github.com/events')
|
||||
|
||||
Now, we have a :class:`Response <requests.Response>` object called ``r``. We can
|
||||
get all the information we need from this object.
|
||||
|
||||
Requests' simple API means that all forms of HTTP request are as obvious. For
|
||||
example, this is how you make an HTTP POST request::
|
||||
|
||||
>>> r = requests.post('https://httpbin.org/post', data={'key': 'value'})
|
||||
|
||||
Nice, right? What about the other HTTP request types: PUT, DELETE, HEAD and
|
||||
OPTIONS? These are all just as simple::
|
||||
|
||||
>>> r = requests.put('https://httpbin.org/put', data={'key': 'value'})
|
||||
>>> r = requests.delete('https://httpbin.org/delete')
|
||||
>>> r = requests.head('https://httpbin.org/get')
|
||||
>>> r = requests.options('https://httpbin.org/get')
|
||||
|
||||
That's all well and good, but it's also only the start of what Requests can
|
||||
do.
|
||||
|
||||
|
||||
Passing Parameters In URLs
|
||||
--------------------------
|
||||
|
||||
You often want to send some sort of data in the URL's query string. If
|
||||
you were constructing the URL by hand, this data would be given as key/value
|
||||
pairs in the URL after a question mark, e.g. ``httpbin.org/get?key=val``.
|
||||
Requests allows you to provide these arguments as a dictionary of strings,
|
||||
using the ``params`` keyword argument. As an example, if you wanted to pass
|
||||
``key1=value1`` and ``key2=value2`` to ``httpbin.org/get``, you would use the
|
||||
following code::
|
||||
|
||||
>>> payload = {'key1': 'value1', 'key2': 'value2'}
|
||||
>>> r = requests.get('https://httpbin.org/get', params=payload)
|
||||
|
||||
You can see that the URL has been correctly encoded by printing the URL::
|
||||
|
||||
>>> print(r.url)
|
||||
https://httpbin.org/get?key2=value2&key1=value1
|
||||
|
||||
Note that any dictionary key whose value is ``None`` will not be added to the
|
||||
URL's query string.
|
||||
|
||||
You can also pass a list of items as a value::
|
||||
|
||||
>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
|
||||
|
||||
>>> r = requests.get('https://httpbin.org/get', params=payload)
|
||||
>>> print(r.url)
|
||||
https://httpbin.org/get?key1=value1&key2=value2&key2=value3
|
||||
|
||||
Response Content
|
||||
----------------
|
||||
|
||||
We can read the content of the server's response. Consider the GitHub timeline
|
||||
again::
|
||||
|
||||
>>> import requests
|
||||
|
||||
>>> r = requests.get('https://api.github.com/events')
|
||||
>>> r.text
|
||||
'[{"repository":{"open_issues":0,"url":"https://github.com/...
|
||||
|
||||
Requests will automatically decode content from the server. Most unicode
|
||||
charsets are seamlessly decoded.
|
||||
|
||||
When you make a request, Requests makes educated guesses about the encoding of
|
||||
the response based on the HTTP headers. The text encoding guessed by Requests
|
||||
is used when you access ``r.text``. You can find out what encoding Requests is
|
||||
using, and change it, using the ``r.encoding`` property::
|
||||
|
||||
>>> r.encoding
|
||||
'utf-8'
|
||||
>>> r.encoding = 'ISO-8859-1'
|
||||
|
||||
If you change the encoding, Requests will use the new value of ``r.encoding``
|
||||
whenever you call ``r.text``. You might want to do this in any situation where
|
||||
you can apply special logic to work out what the encoding of the content will
|
||||
be. For example, HTML and XML have the ability to specify their encoding in
|
||||
their body. In situations like this, you should use ``r.content`` to find the
|
||||
encoding, and then set ``r.encoding``. This will let you use ``r.text`` with
|
||||
the correct encoding.
|
||||
|
||||
Requests will also use custom encodings in the event that you need them. If
|
||||
you have created your own encoding and registered it with the ``codecs``
|
||||
module, you can simply use the codec name as the value of ``r.encoding`` and
|
||||
Requests will handle the decoding for you.
|
||||
|
||||
Binary Response Content
|
||||
-----------------------
|
||||
|
||||
You can also access the response body as bytes, for non-text requests::
|
||||
We can read the content of the server's response::
|
||||
|
||||
>>> r.content
|
||||
b'[{"repository":{"open_issues":0,"url":"https://github.com/...
|
||||
'[{"repository":{"open_issues":0,"url":"https://github.com/...
|
||||
|
||||
The ``gzip`` and ``deflate`` transfer-encodings are automatically decoded for you.
|
||||
Requests does its best to decode content from the server. Most unicode
|
||||
charsets, ``gzip``, and ``deflate`` encodings are all seamlessly decoded.
|
||||
|
||||
The ``br`` transfer-encoding is automatically decoded for you if a Brotli library
|
||||
like `brotli <https://pypi.org/project/brotli>`_ or `brotlicffi <https://pypi.org/project/brotlicffi>`_ is installed.
|
||||
|
||||
For example, to create an image from binary data returned by a request, you can
|
||||
use the following code::
|
||||
|
||||
>>> from PIL import Image
|
||||
>>> from io import BytesIO
|
||||
|
||||
>>> i = Image.open(BytesIO(r.content))
|
||||
When you make a request, ``r.encoding`` is set, based on the HTTP headers.
|
||||
Requests will attempt to use that encoding when you access ``r.content``. You
|
||||
can manually set ``r.encoding`` to any encoding you'd like (including ``None``),
|
||||
and that charset will be used.
|
||||
|
||||
|
||||
JSON Response Content
|
||||
---------------------
|
||||
Make a POST Request
|
||||
-------------------
|
||||
|
||||
There's also a builtin JSON decoder, in case you're dealing with JSON data::
|
||||
POST requests are equally simple::
|
||||
|
||||
>>> import requests
|
||||
|
||||
>>> r = requests.get('https://api.github.com/events')
|
||||
>>> r.json()
|
||||
[{'repository': {'open_issues': 0, 'url': 'https://github.com/...
|
||||
|
||||
In case the JSON decoding fails, ``r.json()`` raises an exception. For example, if
|
||||
the response gets a 204 (No Content), or if the response contains invalid JSON,
|
||||
attempting ``r.json()`` raises ``requests.exceptions.JSONDecodeError``. This wrapper exception
|
||||
provides interoperability for multiple exceptions that may be thrown by different
|
||||
python versions and json serialization libraries.
|
||||
|
||||
It should be noted that the success of the call to ``r.json()`` does **not**
|
||||
indicate the success of the response. Some servers may return a JSON object in a
|
||||
failed response (e.g. error details with HTTP 500). Such JSON will be decoded
|
||||
and returned. To check that a request is successful, use
|
||||
``r.raise_for_status()`` or check ``r.status_code`` is what you expect.
|
||||
r = requests.post("http://httpbin.org/post")
|
||||
|
||||
|
||||
Raw Response Content
|
||||
--------------------
|
||||
Typically, you want to send some form-encoded data — much like an HTML form.
|
||||
To do this, simply pass a dictionary to the `data` argument. Your
|
||||
dictionary of data will automatically be form-encoded when the request is made::
|
||||
|
||||
In the rare case that you'd like to get the raw socket response from the
|
||||
server, you can access ``r.raw``. If you want to do this, make sure you set
|
||||
``stream=True`` in your initial request. Once you do, you can do this::
|
||||
>>> payload = {'key1': 'value1', 'key2': 'value2'}
|
||||
>>> r = requests.post("http://httpbin.org/post", data=payload)
|
||||
>>> print r.content
|
||||
{
|
||||
"origin": "179.13.100.4",
|
||||
"files": {},
|
||||
"form": {
|
||||
"key2": "value2",
|
||||
"key1": "value1"
|
||||
},
|
||||
"url": "http://httpbin.org/post",
|
||||
"args": {},
|
||||
"headers": {
|
||||
"Content-Length": "23",
|
||||
"Accept-Encoding": "identity, deflate, compress, gzip",
|
||||
"Accept": "*/*",
|
||||
"User-Agent": "python-requests/0.8.0",
|
||||
"Host": "127.0.0.1:7077",
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
"data": ""
|
||||
}
|
||||
|
||||
>>> r = requests.get('https://api.github.com/events', stream=True)
|
||||
There are many times that you want to send data that is not form-encoded. If you pass in a ``string`` instead of a ``dict``, that data will be posted directly.
|
||||
|
||||
>>> r.raw
|
||||
<urllib3.response.HTTPResponse object at 0x101194810>
|
||||
For example, the GitHub API v3 accepts JSON-Encoded POST/PATCH data::
|
||||
|
||||
>>> r.raw.read(10)
|
||||
b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'
|
||||
url = 'https://api.github.com/some/endpoint'
|
||||
payload = {'some': 'data'}
|
||||
|
||||
In general, however, you should use a pattern like this to save what is being
|
||||
streamed to a file::
|
||||
r = requests.post(url, data=json.dumps(payload))
|
||||
|
||||
with open(filename, 'wb') as fd:
|
||||
for chunk in r.iter_content(chunk_size=128):
|
||||
fd.write(chunk)
|
||||
|
||||
Using ``Response.iter_content`` will handle a lot of what you would otherwise
|
||||
have to handle when using ``Response.raw`` directly. When streaming a
|
||||
download, the above is the preferred and recommended way to retrieve the
|
||||
content. Note that ``chunk_size`` can be freely adjusted to a number that
|
||||
may better fit your use cases.
|
||||
|
||||
.. note::
|
||||
|
||||
An important note about using ``Response.iter_content`` versus ``Response.raw``.
|
||||
``Response.iter_content`` will automatically decode the ``gzip`` and ``deflate``
|
||||
transfer-encodings. ``Response.raw`` is a raw stream of bytes -- it does not
|
||||
transform the response content. If you really need access to the bytes as they
|
||||
were returned, use ``Response.raw``.
|
||||
|
||||
|
||||
.. _custom-headers:
|
||||
|
||||
Custom Headers
|
||||
--------------
|
||||
@@ -209,169 +99,76 @@ Custom Headers
|
||||
If you'd like to add HTTP headers to a request, simply pass in a ``dict`` to the
|
||||
``headers`` parameter.
|
||||
|
||||
For example, we didn't specify our user-agent in the previous example::
|
||||
For example, we didn't specify our content-type in the previous example::
|
||||
|
||||
>>> url = 'https://api.github.com/some/endpoint'
|
||||
>>> headers = {'user-agent': 'my-app/0.0.1'}
|
||||
url = 'https://api.github.com/some/endpoint'
|
||||
payload = {'some': 'data'}
|
||||
headers = {'content-type': 'application/json'}
|
||||
|
||||
>>> r = requests.get(url, headers=headers)
|
||||
r = requests.post(url, data=json.dumps(payload), headers=headers)
|
||||
|
||||
Note: Custom headers are given less precedence than more specific sources of information. For instance:
|
||||
|
||||
* Authorization headers set with `headers=` will be overridden if credentials
|
||||
are specified in ``.netrc``, which in turn will be overridden by the ``auth=``
|
||||
parameter. Requests will search for the netrc file at `~/.netrc`, `~/_netrc`,
|
||||
or at the path specified by the `NETRC` environment variable.
|
||||
Check details in :ref:`netrc authentication <authentication>`.
|
||||
* Authorization headers will be removed if you get redirected off-host.
|
||||
* Proxy-Authorization headers will be overridden by proxy credentials provided in the URL.
|
||||
* Content-Length headers will be overridden when we can determine the length of the content.
|
||||
|
||||
Furthermore, Requests does not change its behavior at all based on which custom headers are specified. The headers are simply passed on into the final request.
|
||||
|
||||
Note: All header values must be a ``string``, bytestring, or unicode. While permitted, it's advised to avoid passing unicode header values.
|
||||
|
||||
More complicated POST requests
|
||||
------------------------------
|
||||
|
||||
Typically, you want to send some form-encoded data — much like an HTML form.
|
||||
To do this, simply pass a dictionary to the ``data`` argument. Your
|
||||
dictionary of data will automatically be form-encoded when the request is made::
|
||||
|
||||
>>> payload = {'key1': 'value1', 'key2': 'value2'}
|
||||
|
||||
>>> r = requests.post('https://httpbin.org/post', data=payload)
|
||||
>>> print(r.text)
|
||||
{
|
||||
...
|
||||
"form": {
|
||||
"key2": "value2",
|
||||
"key1": "value1"
|
||||
},
|
||||
...
|
||||
}
|
||||
|
||||
The ``data`` argument can also have multiple values for each key. This can be
|
||||
done by making ``data`` either a list of tuples or a dictionary with lists
|
||||
as values. This is particularly useful when the form has multiple elements that
|
||||
use the same key::
|
||||
|
||||
>>> payload_tuples = [('key1', 'value1'), ('key1', 'value2')]
|
||||
>>> r1 = requests.post('https://httpbin.org/post', data=payload_tuples)
|
||||
>>> payload_dict = {'key1': ['value1', 'value2']}
|
||||
>>> r2 = requests.post('https://httpbin.org/post', data=payload_dict)
|
||||
>>> print(r1.text)
|
||||
{
|
||||
...
|
||||
"form": {
|
||||
"key1": [
|
||||
"value1",
|
||||
"value2"
|
||||
]
|
||||
},
|
||||
...
|
||||
}
|
||||
>>> # httpbin may embed non-deterministic metadata,
|
||||
>>> # so we only compare our submitted data here.
|
||||
>>> r1.json()['form'] == r2.json()['form']
|
||||
True
|
||||
|
||||
There are times that you may want to send data that is not form-encoded. If
|
||||
you pass in a ``string`` instead of a ``dict``, that data will be posted directly.
|
||||
|
||||
For example, the GitHub API v3 accepts JSON-Encoded POST/PATCH data::
|
||||
|
||||
>>> import json
|
||||
|
||||
>>> url = 'https://api.github.com/some/endpoint'
|
||||
>>> payload = {'some': 'data'}
|
||||
|
||||
>>> r = requests.post(url, data=json.dumps(payload))
|
||||
|
||||
Please note that the above code will NOT add the ``Content-Type`` header
|
||||
(so in particular it will NOT set it to ``application/json``).
|
||||
|
||||
If you need that header set and you don't want to encode the ``dict`` yourself,
|
||||
you can also pass it directly using the ``json`` parameter (added in version 2.4.2)
|
||||
and it will be encoded automatically:
|
||||
|
||||
>>> url = 'https://api.github.com/some/endpoint'
|
||||
>>> payload = {'some': 'data'}
|
||||
|
||||
>>> r = requests.post(url, json=payload)
|
||||
|
||||
Note, the ``json`` parameter is ignored if either ``data`` or ``files`` is passed.
|
||||
|
||||
POST a Multipart-Encoded File
|
||||
-----------------------------
|
||||
|
||||
Requests makes it simple to upload Multipart-encoded files::
|
||||
|
||||
>>> url = 'https://httpbin.org/post'
|
||||
>>> files = {'file': open('report.xls', 'rb')}
|
||||
>>> url = 'http://httpbin.org/post'
|
||||
>>> files = {'report.xls': open('report.xls', 'rb')}
|
||||
|
||||
>>> r = requests.post(url, files=files)
|
||||
>>> r.text
|
||||
>>> r.content
|
||||
{
|
||||
...
|
||||
"origin": "179.13.100.4",
|
||||
"files": {
|
||||
"report.xls": "<censored...binary...data>"
|
||||
},
|
||||
"form": {},
|
||||
"url": "http://httpbin.org/post",
|
||||
"args": {},
|
||||
"headers": {
|
||||
"Content-Length": "3196",
|
||||
"Accept-Encoding": "identity, deflate, compress, gzip",
|
||||
"Accept": "*/*",
|
||||
"User-Agent": "python-requests/0.8.0",
|
||||
"Host": "httpbin.org:80",
|
||||
"Content-Type": "multipart/form-data; boundary=127.0.0.1.502.21746.1321131593.786.1"
|
||||
},
|
||||
"data": ""
|
||||
}
|
||||
|
||||
Setting filename explicitly::
|
||||
|
||||
>>> url = 'http://httpbin.org/post'
|
||||
>>> files = {'file': ('report.xls', open('report.xls', 'rb'))}
|
||||
|
||||
>>> r = requests.post(url, files=files)
|
||||
>>> r.content
|
||||
{
|
||||
"origin": "179.13.100.4",
|
||||
"files": {
|
||||
"file": "<censored...binary...data>"
|
||||
},
|
||||
...
|
||||
}
|
||||
|
||||
You can set the filename, content_type and headers explicitly::
|
||||
|
||||
>>> url = 'https://httpbin.org/post'
|
||||
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})}
|
||||
|
||||
>>> r = requests.post(url, files=files)
|
||||
>>> r.text
|
||||
{
|
||||
...
|
||||
"files": {
|
||||
"file": "<censored...binary...data>"
|
||||
"form": {},
|
||||
"url": "http://httpbin.org/post",
|
||||
"args": {},
|
||||
"headers": {
|
||||
"Content-Length": "3196",
|
||||
"Accept-Encoding": "identity, deflate, compress, gzip",
|
||||
"Accept": "*/*",
|
||||
"User-Agent": "python-requests/0.8.0",
|
||||
"Host": "httpbin.org:80",
|
||||
"Content-Type": "multipart/form-data; boundary=127.0.0.1.502.21746.1321131593.786.1"
|
||||
},
|
||||
...
|
||||
"data": ""
|
||||
}
|
||||
|
||||
If you want, you can send strings to be received as files::
|
||||
|
||||
>>> url = 'https://httpbin.org/post'
|
||||
>>> files = {'file': ('report.csv', 'some,data,to,send\nanother,row,to,send\n')}
|
||||
|
||||
>>> r = requests.post(url, files=files)
|
||||
>>> r.text
|
||||
{
|
||||
...
|
||||
"files": {
|
||||
"file": "some,data,to,send\\nanother,row,to,send\\n"
|
||||
},
|
||||
...
|
||||
}
|
||||
|
||||
In the event you are posting a very large file as a ``multipart/form-data``
|
||||
request, you may want to stream the request. By default, ``requests`` does not
|
||||
support this, but there is a separate package which does -
|
||||
``requests-toolbelt``. You should read `the toolbelt's documentation
|
||||
<https://toolbelt.readthedocs.io>`_ for more details about how to use it.
|
||||
|
||||
For sending multiple files in one request refer to the :ref:`advanced <advanced>`
|
||||
section.
|
||||
|
||||
.. warning:: It is strongly recommended that you open files in :ref:`binary
|
||||
mode <tut-files>`. This is because Requests may attempt to provide
|
||||
the ``Content-Length`` header for you, and if it does this value
|
||||
will be set to the number of *bytes* in the file. Errors may occur
|
||||
if you open the file in *text mode*.
|
||||
|
||||
|
||||
Response Status Codes
|
||||
---------------------
|
||||
|
||||
We can check the response status code::
|
||||
|
||||
>>> r = requests.get('https://httpbin.org/get')
|
||||
>>> r.status_code
|
||||
200
|
||||
|
||||
@@ -381,22 +178,20 @@ reference::
|
||||
>>> r.status_code == requests.codes.ok
|
||||
True
|
||||
|
||||
If we made a bad request (a 4XX client error or 5XX server error response), we
|
||||
can raise it with
|
||||
:meth:`Response.raise_for_status() <requests.Response.raise_for_status>`::
|
||||
If we made a bad request (non-200 response), we can raise it with
|
||||
:class:`Response.raise_for_status()`::
|
||||
|
||||
>>> bad_r = requests.get('https://httpbin.org/status/404')
|
||||
>>> bad_r.status_code
|
||||
>>> _r = requests.get('http://httpbin.org/status/404')
|
||||
>>> _r.status_code
|
||||
404
|
||||
|
||||
>>> bad_r.raise_for_status()
|
||||
>>> _r.raise_for_status()
|
||||
Traceback (most recent call last):
|
||||
File "requests/models.py", line 832, in raise_for_status
|
||||
raise http_error
|
||||
requests.exceptions.HTTPError: 404 Client Error
|
||||
File "requests/models.py", line 394, in raise_for_status
|
||||
raise self.error
|
||||
urllib2.HTTPError: HTTP Error 404: NOT FOUND
|
||||
|
||||
But, since our ``status_code`` for ``r`` was ``200``, when we call
|
||||
``raise_for_status()`` we get::
|
||||
But, since our ``status_code`` was ``200``, when we call it::
|
||||
|
||||
>>> r.raise_for_status()
|
||||
None
|
||||
@@ -407,121 +202,133 @@ All is well.
|
||||
Response Headers
|
||||
----------------
|
||||
|
||||
We can view the server's response headers using a Python dictionary::
|
||||
We can view the server's response headers with a simple Python dictionary
|
||||
interface::
|
||||
|
||||
>>> r.headers
|
||||
{
|
||||
'status': '200 OK',
|
||||
'content-encoding': 'gzip',
|
||||
'transfer-encoding': 'chunked',
|
||||
'connection': 'close',
|
||||
'server': 'nginx/1.0.4',
|
||||
'x-runtime': '148ms',
|
||||
'etag': '"e1ca502697e5c9317743dc078f67693f"',
|
||||
'content-type': 'application/json'
|
||||
'content-type': 'application/json; charset=utf-8'
|
||||
}
|
||||
|
||||
The dictionary is special, though: it's made just for HTTP headers. According to
|
||||
`RFC 7230 <https://tools.ietf.org/html/rfc7230#section-3.2>`_, HTTP Header names
|
||||
are case-insensitive.
|
||||
`RFC 2616 <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html>`_, HTTP
|
||||
Headers are case-insensitive.
|
||||
|
||||
So, we can access the headers using any capitalization we want::
|
||||
|
||||
>>> r.headers['Content-Type']
|
||||
'application/json'
|
||||
'application/json; charset=utf-8'
|
||||
|
||||
>>> r.headers.get('content-type')
|
||||
'application/json'
|
||||
'application/json; charset=utf-8'
|
||||
|
||||
It is also special in that the server could have sent the same header multiple
|
||||
times with different values, but requests combines them so they can be
|
||||
represented in the dictionary within a single mapping, as per
|
||||
`RFC 7230 <https://tools.ietf.org/html/rfc7230#section-3.2>`_:
|
||||
If a header doesn't exist in the Response, its value defaults to ``None``::
|
||||
|
||||
>>> r.headers['X-Random']
|
||||
None
|
||||
|
||||
A recipient MAY combine multiple header fields with the same field name
|
||||
into one "field-name: field-value" pair, without changing the semantics
|
||||
of the message, by appending each subsequent field value to the combined
|
||||
field value in order, separated by a comma.
|
||||
|
||||
Cookies
|
||||
-------
|
||||
|
||||
If a response contains some Cookies, you can quickly access them::
|
||||
If a response contains some Cookies, you can get quick access to them::
|
||||
|
||||
>>> url = 'http://example.com/some/cookie/setting/url'
|
||||
>>> url = 'http://httpbin.org/cookies/set/requests-is/awesome'
|
||||
>>> r = requests.get(url)
|
||||
|
||||
>>> r.cookies['example_cookie_name']
|
||||
'example_cookie_value'
|
||||
>>> print r.cookies
|
||||
{'requests-is': 'awesome'}
|
||||
|
||||
To send your own cookies to the server, you can use the ``cookies``
|
||||
parameter::
|
||||
|
||||
>>> url = 'https://httpbin.org/cookies'
|
||||
>>> url = 'http://httpbin.org/cookies'
|
||||
>>> cookies = dict(cookies_are='working')
|
||||
|
||||
>>> r = requests.get(url, cookies=cookies)
|
||||
>>> r.text
|
||||
>>> r.content
|
||||
'{"cookies": {"cookies_are": "working"}}'
|
||||
|
||||
Cookies are returned in a :class:`~requests.cookies.RequestsCookieJar`,
|
||||
which acts like a ``dict`` but also offers a more complete interface,
|
||||
suitable for use over multiple domains or paths. Cookie jars can
|
||||
also be passed in to requests::
|
||||
|
||||
>>> jar = requests.cookies.RequestsCookieJar()
|
||||
>>> jar.set('tasty_cookie', 'yum', domain='httpbin.org', path='/cookies')
|
||||
>>> jar.set('gross_cookie', 'blech', domain='httpbin.org', path='/elsewhere')
|
||||
>>> url = 'https://httpbin.org/cookies'
|
||||
>>> r = requests.get(url, cookies=jar)
|
||||
>>> r.text
|
||||
'{"cookies": {"tasty_cookie": "yum"}}'
|
||||
Basic Authentication
|
||||
--------------------
|
||||
|
||||
Most web services require authentication. There many different types of
|
||||
authentication, but the most common is HTTP Basic Auth.
|
||||
|
||||
Making requests with Basic Auth is extremely simple::
|
||||
|
||||
>>> from requests.auth import HTTPBasicAuth
|
||||
>>> requests.get('https://api.github.com/user', auth=HTTPBasicAuth('user', 'pass'))
|
||||
<Response [200]>
|
||||
|
||||
Due to the prevalence of HTTP Basic Auth, requests provides a shorthand for
|
||||
this authentication method::
|
||||
|
||||
>>> requests.get('https://api.github.com/user', auth=('user', 'pass'))
|
||||
<Response [200]>
|
||||
|
||||
Providing the credentials as a tuple in this fashion is functionally equivalent
|
||||
to the ``HTTPBasicAuth`` example above.
|
||||
|
||||
|
||||
Digest Authentication
|
||||
---------------------
|
||||
|
||||
Another popular form of web service protection is Digest Authentication::
|
||||
|
||||
>>> from requests.auth import HTTPDigestAuth
|
||||
>>> url = 'http://httpbin.org/digest-auth/auth/user/pass'
|
||||
>>> requests.get(url, auth=HTTPDigestAuth('user', 'pass'))
|
||||
<Response [200]>
|
||||
|
||||
|
||||
OAuth Authentication
|
||||
--------------------
|
||||
|
||||
Miguel Araujo's `requests-oauth <http://pypi.python.org/pypi/requests-oauth>`_ project provides a simple interface for
|
||||
establishing OAuth connections. Documentation and examples can be found on the requests-oauth `git repository <https://github.com/maraujop/requests-oauth>`_.
|
||||
|
||||
|
||||
Redirection and History
|
||||
-----------------------
|
||||
|
||||
By default Requests will perform location redirection for all verbs except
|
||||
HEAD.
|
||||
Requests will automatically perform location redirection while using idempotent methods.
|
||||
|
||||
We can use the ``history`` property of the Response object to track redirection.
|
||||
|
||||
The :attr:`Response.history <requests.Response.history>` list contains the
|
||||
:class:`Response <requests.Response>` objects that were created in order to
|
||||
complete the request. The list is sorted from the oldest to the most recent
|
||||
response.
|
||||
|
||||
For example, GitHub redirects all HTTP requests to HTTPS::
|
||||
|
||||
>>> r = requests.get('http://github.com/')
|
||||
GitHub redirects all HTTP requests to HTTPS. Let's see what happens::
|
||||
|
||||
>>> r = requests.get('http://github.com')
|
||||
>>> r.url
|
||||
'https://github.com/'
|
||||
|
||||
>>> r.status_code
|
||||
200
|
||||
|
||||
>>> r.history
|
||||
[<Response [301]>]
|
||||
|
||||
The :class:`Response.history` list contains a list of the
|
||||
:class:`Request` objects that were created in order to complete the request.
|
||||
|
||||
If you're using GET, OPTIONS, POST, PUT, PATCH or DELETE, you can disable
|
||||
redirection handling with the ``allow_redirects`` parameter::
|
||||
|
||||
>>> r = requests.get('http://github.com/', allow_redirects=False)
|
||||
If you're using GET, HEAD, or OPTIONS, you can disable redirection
|
||||
handling with the ``allow_redirects`` parameter::
|
||||
|
||||
>>> r = requests.get('http://github.com', allow_redirects=False)
|
||||
>>> r.status_code
|
||||
301
|
||||
|
||||
>>> r.history
|
||||
[]
|
||||
|
||||
If you're using HEAD, you can enable redirection as well::
|
||||
|
||||
>>> r = requests.head('http://github.com/', allow_redirects=True)
|
||||
If you're using POST, PUT, PATCH, *&c*, you can also explicitly enable redirection as well::
|
||||
|
||||
>>> r = requests.post('http://github.com', allow_redirects=True)
|
||||
>>> r.url
|
||||
'https://github.com/'
|
||||
|
||||
>>> r.history
|
||||
[<Response [301]>]
|
||||
|
||||
@@ -529,44 +336,37 @@ If you're using HEAD, you can enable redirection as well::
|
||||
Timeouts
|
||||
--------
|
||||
|
||||
You can tell Requests to stop waiting for a response after a given number of
|
||||
seconds with the ``timeout`` parameter. Nearly all production code should use
|
||||
this parameter in nearly all requests. Failure to do so can cause your program
|
||||
to hang indefinitely::
|
||||
You can tell requests to stop waiting for a response after a given number of seconds with the ``timeout`` parameter::
|
||||
|
||||
>>> requests.get('https://github.com/', timeout=0.001)
|
||||
>>> requests.get('http://github.com', timeout=0.001)
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
requests.exceptions.Timeout: HTTPConnectionPool(host='github.com', port=80): Request timed out. (timeout=0.001)
|
||||
|
||||
requests.exceptions.Timeout: Request timed out.
|
||||
|
||||
.. admonition:: Note
|
||||
|
||||
``timeout`` is not a time limit on the entire response download;
|
||||
rather, an exception is raised if the server has not issued a
|
||||
response for ``timeout`` seconds (more precisely, if no bytes have been
|
||||
received on the underlying socket for ``timeout`` seconds). If no timeout is specified explicitly, requests do
|
||||
not time out.
|
||||
``timeout`` only effects the connection process itself, not the downloading of the response body.
|
||||
|
||||
|
||||
Errors and Exceptions
|
||||
---------------------
|
||||
|
||||
In the event of a network problem (e.g. DNS failure, refused connection, etc),
|
||||
Requests will raise a :exc:`~requests.exceptions.ConnectionError` exception.
|
||||
Requests will raise a :class:`ConnectionError` exception.
|
||||
|
||||
:meth:`Response.raise_for_status() <requests.Response.raise_for_status>` will
|
||||
raise an :exc:`~requests.exceptions.HTTPError` if the HTTP request
|
||||
returned an unsuccessful status code.
|
||||
In the event of the rare invalid HTTP response, Requests will raise
|
||||
an :class:`HTTPError` exception.
|
||||
|
||||
If a request times out, a :exc:`~requests.exceptions.Timeout` exception is
|
||||
raised.
|
||||
If a request times out, a :class:`Timeout` exception is raised.
|
||||
|
||||
If a request exceeds the configured number of maximum redirections, a
|
||||
:exc:`~requests.exceptions.TooManyRedirects` exception is raised.
|
||||
If a request exceeds the configured number of maximum redirections, a :class:`TooManyRedirects` exception is raised.
|
||||
|
||||
All exceptions that Requests explicitly raises inherit from
|
||||
:exc:`requests.exceptions.RequestException`.
|
||||
:class:`requests.exceptions.RequestException`.
|
||||
|
||||
You can refer to :ref:`Configuration API Docs <configurations>` for immediate raising of :class:`HTTPError` exceptions
|
||||
via the ``danger_mode`` option or have Requests catch the majority of :class:`requests.exceptions.RequestException` exceptions
|
||||
with the ``safe_mode`` option.
|
||||
|
||||
-----------------------
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Copyright 2019 Kenneth Reitz. All rights reserved.
|
||||
BIN
ext/kr.png
|
Before Width: | Height: | Size: 9.2 KiB |
BIN
ext/psf.png
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 188 KiB |
9773
ext/requests-logo.ai
|
Before Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 863 KiB |
125
pyproject.toml
@@ -1,125 +0,0 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "requests"
|
||||
description = "Python HTTP for Humans."
|
||||
readme = "README.md"
|
||||
license = {text = "Apache-2.0"}
|
||||
authors = [
|
||||
{ name = "Kenneth Reitz", email = "me@kennethreitz.org" },
|
||||
]
|
||||
maintainers = [
|
||||
{name = "Ian Stapleton Cordasco", email="graffatcolmingov@gmail.com"},
|
||||
{name = "Nate Prewitt", email="nate.prewitt@gmail.com"}
|
||||
]
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"charset_normalizer>=2,<4",
|
||||
"idna>=2.5,<4",
|
||||
"urllib3>=1.26,<3",
|
||||
"certifi>=2023.5.7"
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: Web Environment",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Programming Language :: Python :: 3.15",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Programming Language :: Python :: Free Threading :: 2 - Beta",
|
||||
"Topic :: Internet :: WWW/HTTP",
|
||||
"Topic :: Software Development :: Libraries"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Documentation = "https://requests.readthedocs.io"
|
||||
Source = "https://github.com/psf/requests"
|
||||
|
||||
[project.optional-dependencies]
|
||||
security = []
|
||||
socks = ["PySocks>=1.5.6, !=1.5.7"]
|
||||
use_chardet_on_py3 = ["chardet>=3.0.2,<8"]
|
||||
|
||||
[dependency-groups]
|
||||
test = [
|
||||
"requests[socks]",
|
||||
"pytest-httpbin==2.1.0",
|
||||
"httpbin~=0.10.0",
|
||||
"pytest-cov",
|
||||
"pytest-mock",
|
||||
"pytest-xdist",
|
||||
"pytest>=3",
|
||||
"trustme",
|
||||
]
|
||||
typecheck = [
|
||||
"pyright",
|
||||
"typing_extensions",
|
||||
"chardet",
|
||||
"cryptography",
|
||||
"PyOpenSSL",
|
||||
]
|
||||
|
||||
[tool.setuptools]
|
||||
license-files = ["LICENSE", "NOTICE"]
|
||||
|
||||
[tool.setuptools.dynamic]
|
||||
version = {attr = "requests.__version__.__version__"}
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py310"
|
||||
src = ["src/requests", "tests"]
|
||||
exclude = ["docs/", "ext/"]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"E", # pycodestyle errors
|
||||
"W", # pycodestyle warnings
|
||||
"F", # pyflakes
|
||||
"I", # isort
|
||||
"UP", # pyupgrade
|
||||
"T10", # flake8-debugger (replaces debug-statements hook)
|
||||
]
|
||||
ignore = ["E203", "E501", "UP031"]
|
||||
per-file-ignores = {"src/requests/__init__.py" = ["E402", "F401"], "src/requests/compat.py" = ["E402", "F401"], "tests/compat.py" = ["F401"]}
|
||||
|
||||
[tool.ruff.lint.isort]
|
||||
known-first-party = ["requests"]
|
||||
|
||||
[tool.ruff.format]
|
||||
# Use black-compatible formatting
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "--doctest-modules"
|
||||
doctest_optionflags = "NORMALIZE_WHITESPACE ELLIPSIS"
|
||||
minversion = "6.2"
|
||||
testpaths = ["tests"]
|
||||
|
||||
|
||||
[tool.pyright]
|
||||
include = ["src/requests"]
|
||||
typeCheckingMode = "strict"
|
||||
# linting rules unrelated to type checking
|
||||
reportPrivateUsage = false
|
||||
reportPrivateImportUsage = false
|
||||
reportUnnecessaryIsInstance = false
|
||||
reportUnusedImport = false # duplicate of Ruff's F401 rule
|
||||
34
requests/__init__.py
Normal file
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# __
|
||||
# /__) _ _ _ _ _/ _
|
||||
# / ( (- (/ (/ (- _) / _)
|
||||
# /
|
||||
|
||||
"""
|
||||
requests
|
||||
~~~~~~~~
|
||||
|
||||
:copyright: (c) 2012 by Kenneth Reitz.
|
||||
:license: ISC, see LICENSE for more details.
|
||||
|
||||
"""
|
||||
|
||||
__title__ = 'requests'
|
||||
__version__ = '0.9.2'
|
||||
__build__ = 0x000902
|
||||
__author__ = 'Kenneth Reitz'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = 'Copyright 2012 Kenneth Reitz'
|
||||
|
||||
|
||||
|
||||
from . import utils
|
||||
from .models import Request, Response
|
||||
from .api import request, get, head, post, patch, put, delete, options
|
||||
from .sessions import session, Session
|
||||
from .status_codes import codes
|
||||
from .exceptions import (
|
||||
RequestException, Timeout, URLRequired,
|
||||
TooManyRedirects, HTTPError, ConnectionError
|
||||
)
|
||||
116
requests/api.py
Normal file
@@ -0,0 +1,116 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.api
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This module implements the Requests API.
|
||||
|
||||
:copyright: (c) 2012 by Kenneth Reitz.
|
||||
:license: ISC, see LICENSE for more details.
|
||||
|
||||
"""
|
||||
|
||||
from . import sessions
|
||||
|
||||
def request(method, url, **kwargs):
|
||||
"""Constructs and sends a :class:`Request <Request>`.
|
||||
Returns :class:`Response <Response>` object.
|
||||
|
||||
:param method: method for the new :class:`Request` object.
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
|
||||
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
|
||||
:param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
|
||||
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
|
||||
:param files: (optional) Dictionary of 'name': file-like-objects (or {'name': ('filename', fileobj)}) for multipart encoding upload.
|
||||
:param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
|
||||
:param timeout: (optional) Float describing the timeout of the request.
|
||||
:param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.
|
||||
:param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
|
||||
:param return_response: (optional) If False, an un-sent Request object will returned.
|
||||
:param session: (optional) A :class:`Session` object to be used for the request.
|
||||
:param config: (optional) A configuration dictionary.
|
||||
:param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided.
|
||||
:param prefetch: (optional) if ``True``, the response content will be immediately downloaded.
|
||||
"""
|
||||
|
||||
s = kwargs.pop('session') if 'session' in kwargs else sessions.session()
|
||||
return s.request(method=method, url=url, **kwargs)
|
||||
|
||||
|
||||
|
||||
def get(url, **kwargs):
|
||||
"""Sends a GET request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param **kwargs: Optional arguments that ``request`` takes.
|
||||
"""
|
||||
|
||||
kwargs.setdefault('allow_redirects', True)
|
||||
return request('get', url, **kwargs)
|
||||
|
||||
|
||||
def options(url, **kwargs):
|
||||
"""Sends a OPTIONS request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param **kwargs: Optional arguments that ``request`` takes.
|
||||
"""
|
||||
|
||||
kwargs.setdefault('allow_redirects', True)
|
||||
return request('options', url, **kwargs)
|
||||
|
||||
|
||||
def head(url, **kwargs):
|
||||
"""Sends a HEAD request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param **kwargs: Optional arguments that ``request`` takes.
|
||||
"""
|
||||
|
||||
kwargs.setdefault('allow_redirects', True)
|
||||
return request('head', url, **kwargs)
|
||||
|
||||
|
||||
def post(url, data=None, **kwargs):
|
||||
"""Sends a POST request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
|
||||
:param **kwargs: Optional arguments that ``request`` takes.
|
||||
"""
|
||||
|
||||
return request('post', url, data=data, **kwargs)
|
||||
|
||||
|
||||
def put(url, data=None, **kwargs):
|
||||
"""Sends a PUT request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
|
||||
:param **kwargs: Optional arguments that ``request`` takes.
|
||||
"""
|
||||
|
||||
return request('put', url, data=data, **kwargs)
|
||||
|
||||
|
||||
def patch(url, data=None, **kwargs):
|
||||
"""Sends a PATCH request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
|
||||
:param **kwargs: Optional arguments that ``request`` takes.
|
||||
"""
|
||||
|
||||
return request('patch', url, data=data, **kwargs)
|
||||
|
||||
|
||||
def delete(url, **kwargs):
|
||||
"""Sends a DELETE request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param **kwargs: Optional arguments that ``request`` takes.
|
||||
"""
|
||||
|
||||
return request('delete', url, **kwargs)
|
||||
92
requests/async.py
Normal file
@@ -0,0 +1,92 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.async
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module contains an asynchronous replica of ``requests.api``, powered
|
||||
by gevent. All API methods return a ``Request`` instance (as opposed to
|
||||
``Response``). A list of requests can be sent with ``map()``.
|
||||
"""
|
||||
|
||||
try:
|
||||
import gevent
|
||||
from gevent import monkey as curious_george
|
||||
from gevent.pool import Pool
|
||||
except ImportError:
|
||||
raise RuntimeError('Gevent is required for requests.async.')
|
||||
|
||||
# Monkey-patch.
|
||||
curious_george.patch_all(thread=False)
|
||||
|
||||
from . import api
|
||||
|
||||
|
||||
__all__ = (
|
||||
'map',
|
||||
'get', 'options', 'head', 'post', 'put', 'patch', 'delete', 'request'
|
||||
)
|
||||
|
||||
|
||||
def patched(f):
|
||||
"""Patches a given API function to not send."""
|
||||
|
||||
def wrapped(*args, **kwargs):
|
||||
|
||||
kwargs['return_response'] = False
|
||||
kwargs['prefetch'] = True
|
||||
|
||||
config = kwargs.get('config', {})
|
||||
config.update(safe_mode=True)
|
||||
|
||||
kwargs['config'] = config
|
||||
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def send(r, pool=None):
|
||||
"""Sends the request object using the specified pool. If a pool isn't
|
||||
specified this method blocks. Pools are useful because you can specify size
|
||||
and can hence limit concurrency."""
|
||||
|
||||
if pool != None:
|
||||
return pool.spawn(r.send)
|
||||
|
||||
return gevent.spawn(r.send)
|
||||
|
||||
|
||||
# Patched requests.api functions.
|
||||
get = patched(api.get)
|
||||
options = patched(api.options)
|
||||
head = patched(api.head)
|
||||
post = patched(api.post)
|
||||
put = patched(api.put)
|
||||
patch = patched(api.patch)
|
||||
delete = patched(api.delete)
|
||||
request = patched(api.request)
|
||||
|
||||
|
||||
def map(requests, prefetch=True, size=None):
|
||||
"""Concurrently converts a list of Requests to Responses.
|
||||
|
||||
:param requests: a collection of Request objects.
|
||||
:param prefetch: If False, the content will not be downloaded immediately.
|
||||
:param size: Specifies the number of requests to make at a time. If None, no throttling occurs.
|
||||
"""
|
||||
|
||||
requests = list(requests)
|
||||
|
||||
if size:
|
||||
pool = Pool(size)
|
||||
jobs = [pool.spawn(r.send) for r in requests]
|
||||
else:
|
||||
jobs = [gevent.spawn(r.send) for r in requests]
|
||||
|
||||
gevent.joinall(jobs)
|
||||
|
||||
if prefetch:
|
||||
[r.response.content for r in requests]
|
||||
|
||||
return [r.response for r in requests]
|
||||
138
requests/auth.py
Normal file
@@ -0,0 +1,138 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.auth
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
This module contains the authentication handlers for Requests.
|
||||
"""
|
||||
|
||||
import time
|
||||
import hashlib
|
||||
|
||||
from base64 import b64encode
|
||||
from urlparse import urlparse
|
||||
|
||||
from .utils import randombytes, parse_dict_header
|
||||
|
||||
|
||||
|
||||
def _basic_auth_str(username, password):
|
||||
"""Returns a Basic Auth string."""
|
||||
return 'Basic %s' % b64encode('%s:%s' % (username, password))
|
||||
|
||||
|
||||
class AuthBase(object):
|
||||
"""Base class that all auth implementations derive from"""
|
||||
|
||||
def __call__(self, r):
|
||||
raise NotImplementedError('Auth hooks must be callable.')
|
||||
|
||||
|
||||
class HTTPBasicAuth(AuthBase):
|
||||
"""Attaches HTTP Basic Authentication to the given Request object."""
|
||||
def __init__(self, username, password):
|
||||
self.username = str(username)
|
||||
self.password = str(password)
|
||||
|
||||
def __call__(self, r):
|
||||
r.headers['Authorization'] = _basic_auth_str(self.username, self.password)
|
||||
return r
|
||||
|
||||
|
||||
class HTTPProxyAuth(HTTPBasicAuth):
|
||||
"""Attaches HTTP Proxy Authenetication to a given Request object."""
|
||||
def __call__(self, r):
|
||||
r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password)
|
||||
return r
|
||||
|
||||
|
||||
class HTTPDigestAuth(AuthBase):
|
||||
"""Attaches HTTP Digest Authentication to the given Request object."""
|
||||
def __init__(self, username, password):
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
def handle_401(self, r):
|
||||
"""Takes the given response and tries digest-auth, if needed."""
|
||||
|
||||
s_auth = r.headers.get('www-authenticate', '')
|
||||
|
||||
if 'digest' in s_auth.lower():
|
||||
|
||||
last_nonce = ''
|
||||
nonce_count = 0
|
||||
|
||||
chal = parse_dict_header(s_auth.replace('Digest ', ''))
|
||||
|
||||
realm = chal['realm']
|
||||
nonce = chal['nonce']
|
||||
qop = chal.get('qop')
|
||||
algorithm = chal.get('algorithm', 'MD5')
|
||||
opaque = chal.get('opaque', None)
|
||||
|
||||
algorithm = algorithm.upper()
|
||||
# lambdas assume digest modules are imported at the top level
|
||||
if algorithm == 'MD5':
|
||||
H = lambda x: hashlib.md5(x).hexdigest()
|
||||
elif algorithm == 'SHA':
|
||||
H = lambda x: hashlib.sha1(x).hexdigest()
|
||||
# XXX MD5-sess
|
||||
KD = lambda s, d: H("%s:%s" % (s, d))
|
||||
|
||||
if H is None:
|
||||
return None
|
||||
|
||||
# XXX not implemented yet
|
||||
entdig = None
|
||||
p_parsed = urlparse(r.request.url)
|
||||
path = p_parsed.path
|
||||
if p_parsed.query:
|
||||
path += '?' + p_parsed.query
|
||||
|
||||
A1 = '%s:%s:%s' % (self.username, realm, self.password)
|
||||
A2 = '%s:%s' % (r.request.method, path)
|
||||
|
||||
if qop == 'auth':
|
||||
if nonce == last_nonce:
|
||||
nonce_count += 1
|
||||
else:
|
||||
nonce_count = 1
|
||||
last_nonce = nonce
|
||||
|
||||
ncvalue = '%08x' % nonce_count
|
||||
cnonce = (hashlib.sha1("%s:%s:%s:%s" % (
|
||||
nonce_count, nonce, time.ctime(), randombytes(8)))
|
||||
.hexdigest()[:16]
|
||||
)
|
||||
noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, H(A2))
|
||||
respdig = KD(H(A1), noncebit)
|
||||
elif qop is None:
|
||||
respdig = KD(H(A1), "%s:%s" % (nonce, H(A2)))
|
||||
else:
|
||||
# XXX handle auth-int.
|
||||
return None
|
||||
|
||||
# XXX should the partial digests be encoded too?
|
||||
base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \
|
||||
'response="%s"' % (self.username, realm, nonce, path, respdig)
|
||||
if opaque:
|
||||
base += ', opaque="%s"' % opaque
|
||||
if entdig:
|
||||
base += ', digest="%s"' % entdig
|
||||
base += ', algorithm="%s"' % algorithm
|
||||
if qop:
|
||||
base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce)
|
||||
|
||||
r.request.headers['Authorization'] = 'Digest %s' % (base)
|
||||
r.request.send(anyway=True)
|
||||
_r = r.request.response
|
||||
_r.history.append(r)
|
||||
|
||||
return _r
|
||||
|
||||
return r
|
||||
|
||||
def __call__(self, r):
|
||||
r.hooks['response'] = self.handle_401
|
||||
return r
|
||||
44
requests/defaults.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.defaults
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module provides the Requests configuration defaults.
|
||||
|
||||
Configurations:
|
||||
|
||||
:base_headers: Default HTTP headers.
|
||||
:verbose: Stream to write request logging to.
|
||||
:timeout: Seconds until request timeout.
|
||||
:max_redirects: Maximum number of redirects allowed within a request.
|
||||
:decode_unicode: Decode unicode responses automatically?
|
||||
:keep_alive: Reuse HTTP Connections?
|
||||
:max_retries: The number of times a request should be retried in the event of a connection failure.
|
||||
:danger_mode: If true, Requests will raise errors immediately.
|
||||
:safe_mode: If true, Requests will catch all errors.
|
||||
:pool_maxsize: The maximium size of an HTTP connection pool.
|
||||
:pool_connections: The number of active HTTP connection pools to use.
|
||||
|
||||
"""
|
||||
|
||||
from . import __version__
|
||||
|
||||
defaults = dict()
|
||||
|
||||
|
||||
defaults['base_headers'] = {
|
||||
'User-Agent': 'python-requests/%s' % __version__,
|
||||
'Accept-Encoding': ', '.join(('identity', 'deflate', 'compress', 'gzip')),
|
||||
'Accept': '*/*'
|
||||
}
|
||||
|
||||
defaults['verbose'] = None
|
||||
defaults['max_redirects'] = 30
|
||||
defaults['decode_unicode'] = True
|
||||
defaults['pool_connections'] = 10
|
||||
defaults['pool_maxsize'] = 10
|
||||
defaults['max_retries'] = 0
|
||||
defaults['danger_mode'] = False
|
||||
defaults['safe_mode'] = False
|
||||
defaults['keep_alive'] = True
|
||||
31
requests/exceptions.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.exceptions
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains the set of Requests' exceptions.
|
||||
|
||||
"""
|
||||
|
||||
class RequestException(Exception):
|
||||
"""There was an ambiguous exception that occurred while handling your
|
||||
request."""
|
||||
|
||||
class HTTPError(RequestException):
|
||||
"""An HTTP error occurred."""
|
||||
|
||||
class ConnectionError(RequestException):
|
||||
"""A Connection error occurred."""
|
||||
|
||||
class SSLError(ConnectionError):
|
||||
"""An SSL error occurred."""
|
||||
|
||||
class Timeout(RequestException):
|
||||
"""The request timed out."""
|
||||
|
||||
class URLRequired(RequestException):
|
||||
"""A valid URL is required to make a request."""
|
||||
|
||||
class TooManyRedirects(RequestException):
|
||||
"""Too many redirects."""
|
||||
40
requests/hooks.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.hooks
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This module provides the capabilities for the Requests hooks system.
|
||||
|
||||
Available hooks:
|
||||
|
||||
``args``:
|
||||
A dictionary of the arguments being sent to Request().
|
||||
|
||||
``pre_request``:
|
||||
The Request object, directly before being sent.
|
||||
|
||||
``post_request``:
|
||||
The Request object, directly after being sent.
|
||||
|
||||
``response``:
|
||||
The response generated from a Request.
|
||||
|
||||
"""
|
||||
|
||||
import traceback
|
||||
|
||||
|
||||
def dispatch_hook(key, hooks, hook_data):
|
||||
"""Dispatches a hook dictionary on a given piece of data."""
|
||||
|
||||
hooks = hooks or dict()
|
||||
|
||||
if key in hooks:
|
||||
try:
|
||||
return hooks.get(key).__call__(hook_data) or hook_data
|
||||
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
return hook_data
|
||||
743
requests/models.py
Normal file
@@ -0,0 +1,743 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
requests.models
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains the primary objects that power Requests.
|
||||
"""
|
||||
|
||||
import os
|
||||
import urllib
|
||||
|
||||
from urlparse import urlparse, urlunparse, urljoin, urlsplit
|
||||
from datetime import datetime
|
||||
|
||||
from .hooks import dispatch_hook
|
||||
from .structures import CaseInsensitiveDict
|
||||
from .status_codes import codes
|
||||
from .packages import oreos
|
||||
from .auth import HTTPBasicAuth, HTTPProxyAuth
|
||||
from .packages.urllib3.response import HTTPResponse
|
||||
from .packages.urllib3.exceptions import MaxRetryError
|
||||
from .packages.urllib3.exceptions import SSLError as _SSLError
|
||||
from .packages.urllib3.exceptions import HTTPError as _HTTPError
|
||||
from .packages.urllib3 import connectionpool, poolmanager
|
||||
from .packages.urllib3.filepost import encode_multipart_formdata
|
||||
from .exceptions import (
|
||||
ConnectionError, HTTPError, RequestException, Timeout, TooManyRedirects,
|
||||
URLRequired, SSLError)
|
||||
from .utils import (
|
||||
get_encoding_from_headers, stream_decode_response_unicode,
|
||||
stream_decompress, guess_filename, requote_path)
|
||||
|
||||
|
||||
REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved)
|
||||
|
||||
|
||||
|
||||
class Request(object):
|
||||
"""The :class:`Request <Request>` object. It carries out all functionality of
|
||||
Requests. Recommended interface is with the Requests functions.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
url=None,
|
||||
headers=dict(),
|
||||
files=None,
|
||||
method=None,
|
||||
data=dict(),
|
||||
params=dict(),
|
||||
auth=None,
|
||||
cookies=None,
|
||||
timeout=None,
|
||||
redirect=False,
|
||||
allow_redirects=False,
|
||||
proxies=None,
|
||||
hooks=None,
|
||||
config=None,
|
||||
_poolmanager=None,
|
||||
verify=None):
|
||||
|
||||
#: Float describes the timeout of the request.
|
||||
# (Use socket.setdefaulttimeout() as fallback)
|
||||
self.timeout = timeout
|
||||
|
||||
#: Request URL.
|
||||
self.url = url
|
||||
|
||||
#: Dictionary of HTTP Headers to attach to the :class:`Request <Request>`.
|
||||
self.headers = dict(headers or [])
|
||||
|
||||
#: Dictionary of files to multipart upload (``{filename: content}``).
|
||||
self.files = files
|
||||
|
||||
#: HTTP Method to use.
|
||||
self.method = method
|
||||
|
||||
#: Dictionary or byte of request body data to attach to the
|
||||
#: :class:`Request <Request>`.
|
||||
self.data = None
|
||||
|
||||
#: Dictionary or byte of querystring data to attach to the
|
||||
#: :class:`Request <Request>`.
|
||||
self.params = None
|
||||
|
||||
#: True if :class:`Request <Request>` is part of a redirect chain (disables history
|
||||
#: and HTTPError storage).
|
||||
self.redirect = redirect
|
||||
|
||||
#: Set to True if full redirects are allowed (e.g. re-POST-ing of data at new ``Location``)
|
||||
self.allow_redirects = allow_redirects
|
||||
|
||||
# Dictionary mapping protocol to the URL of the proxy (e.g. {'http': 'foo.bar:3128'})
|
||||
self.proxies = dict(proxies or [])
|
||||
|
||||
self.data, self._enc_data = self._encode_params(data)
|
||||
self.params, self._enc_params = self._encode_params(params)
|
||||
|
||||
#: :class:`Response <Response>` instance, containing
|
||||
#: content and metadata of HTTP Response, once :attr:`sent <send>`.
|
||||
self.response = Response()
|
||||
|
||||
#: Authentication tuple or object to attach to :class:`Request <Request>`.
|
||||
self.auth = auth
|
||||
|
||||
#: CookieJar to attach to :class:`Request <Request>`.
|
||||
self.cookies = dict(cookies or [])
|
||||
|
||||
#: Dictionary of configurations for this request.
|
||||
self.config = dict(config or [])
|
||||
|
||||
#: True if Request has been sent.
|
||||
self.sent = False
|
||||
|
||||
#: Event-handling hooks.
|
||||
self.hooks = hooks
|
||||
|
||||
#: Session.
|
||||
self.session = None
|
||||
|
||||
#: SSL Verification.
|
||||
self.verify = verify
|
||||
|
||||
if headers:
|
||||
headers = CaseInsensitiveDict(self.headers)
|
||||
else:
|
||||
headers = CaseInsensitiveDict()
|
||||
|
||||
# Add configured base headers.
|
||||
for (k, v) in self.config.get('base_headers', {}).items():
|
||||
if k not in headers:
|
||||
headers[k] = v
|
||||
|
||||
self.headers = headers
|
||||
self._poolmanager = _poolmanager
|
||||
|
||||
# Pre-request hook.
|
||||
r = dispatch_hook('pre_request', hooks, self)
|
||||
self.__dict__.update(r.__dict__)
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return '<Request [%s]>' % (self.method)
|
||||
|
||||
|
||||
def _build_response(self, resp, is_error=False):
|
||||
"""Build internal :class:`Response <Response>` object
|
||||
from given response.
|
||||
"""
|
||||
|
||||
def build(resp):
|
||||
|
||||
response = Response()
|
||||
|
||||
# Pass settings over.
|
||||
response.config = self.config
|
||||
|
||||
if resp:
|
||||
|
||||
# Fallback to None if there's no status_code, for whatever reason.
|
||||
response.status_code = getattr(resp, 'status', None)
|
||||
|
||||
# Make headers case-insensitive.
|
||||
response.headers = CaseInsensitiveDict(getattr(resp, 'headers', None))
|
||||
|
||||
# Set encoding.
|
||||
response.encoding = get_encoding_from_headers(response.headers)
|
||||
|
||||
# Start off with our local cookies.
|
||||
cookies = self.cookies or dict()
|
||||
|
||||
# Add new cookies from the server.
|
||||
if 'set-cookie' in response.headers:
|
||||
cookie_header = response.headers['set-cookie']
|
||||
cookies = oreos.dict_from_string(cookie_header)
|
||||
|
||||
# Save cookies in Response.
|
||||
response.cookies = cookies
|
||||
|
||||
# No exceptions were harmed in the making of this request.
|
||||
response.error = getattr(resp, 'error', None)
|
||||
|
||||
# Save original response for later.
|
||||
response.raw = resp
|
||||
|
||||
if is_error:
|
||||
response.error = resp
|
||||
|
||||
response.url = self.full_url
|
||||
|
||||
return response
|
||||
|
||||
history = []
|
||||
|
||||
r = build(resp)
|
||||
cookies = self.cookies
|
||||
self.cookies.update(r.cookies)
|
||||
|
||||
if r.status_code in REDIRECT_STATI and not self.redirect:
|
||||
|
||||
while (
|
||||
('location' in r.headers) and
|
||||
((r.status_code is codes.see_other) or (self.allow_redirects))
|
||||
):
|
||||
|
||||
if not len(history) < self.config.get('max_redirects'):
|
||||
raise TooManyRedirects()
|
||||
|
||||
history.append(r)
|
||||
|
||||
url = r.headers['location']
|
||||
|
||||
# Handle redirection without scheme (see: RFC 1808 Section 4)
|
||||
if url.startswith('//'):
|
||||
parsed_rurl = urlparse(r.url)
|
||||
url = '%s:%s' % (parsed_rurl.scheme, url)
|
||||
|
||||
# Facilitate non-RFC2616-compliant 'location' headers
|
||||
# (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
|
||||
if not urlparse(url).netloc:
|
||||
url = urljoin(r.url, url)
|
||||
|
||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4
|
||||
if r.status_code is codes.see_other:
|
||||
method = 'GET'
|
||||
else:
|
||||
method = self.method
|
||||
|
||||
# Remove the cookie headers that were sent.
|
||||
headers = self.headers
|
||||
try:
|
||||
del headers['Cookie']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
request = Request(
|
||||
url=url,
|
||||
headers=headers,
|
||||
files=self.files,
|
||||
method=method,
|
||||
params=self.session.params,
|
||||
auth=self.auth,
|
||||
cookies=cookies,
|
||||
redirect=True,
|
||||
config=self.config,
|
||||
timeout=self.timeout,
|
||||
_poolmanager=self._poolmanager,
|
||||
proxies = self.proxies,
|
||||
verify = self.verify
|
||||
)
|
||||
|
||||
request.send()
|
||||
cookies.update(request.response.cookies)
|
||||
r = request.response
|
||||
self.cookies.update(r.cookies)
|
||||
|
||||
r.history = history
|
||||
|
||||
self.response = r
|
||||
self.response.request = self
|
||||
self.response.cookies.update(self.cookies)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _encode_params(data):
|
||||
"""Encode parameters in a piece of data.
|
||||
|
||||
If the data supplied is a dictionary, encodes each parameter in it, and
|
||||
returns a list of tuples containing the encoded parameters, and a urlencoded
|
||||
version of that.
|
||||
|
||||
Otherwise, assumes the data is already encoded appropriately, and
|
||||
returns it twice.
|
||||
"""
|
||||
|
||||
if hasattr(data, '__iter__'):
|
||||
data = dict(data)
|
||||
|
||||
if hasattr(data, 'items'):
|
||||
result = []
|
||||
for k, vs in data.items():
|
||||
for v in isinstance(vs, list) and vs or [vs]:
|
||||
result.append((k.encode('utf-8') if isinstance(k, unicode) else k,
|
||||
v.encode('utf-8') if isinstance(v, unicode) else v))
|
||||
return result, urllib.urlencode(result, doseq=True)
|
||||
else:
|
||||
return data, data
|
||||
|
||||
@property
|
||||
def full_url(self):
|
||||
"""Build the actual URL to use."""
|
||||
|
||||
if not self.url:
|
||||
raise URLRequired()
|
||||
|
||||
# Support for unicode domain names and paths.
|
||||
scheme, netloc, path, params, query, fragment = urlparse(self.url)
|
||||
|
||||
if not scheme:
|
||||
raise ValueError("Invalid URL %r: No schema supplied" %self.url)
|
||||
|
||||
netloc = netloc.encode('idna')
|
||||
|
||||
if isinstance(path, unicode):
|
||||
path = path.encode('utf-8')
|
||||
|
||||
path = requote_path(path)
|
||||
|
||||
url = str(urlunparse([ scheme, netloc, path, params, query, fragment ]))
|
||||
|
||||
if self._enc_params:
|
||||
if urlparse(url).query:
|
||||
return '%s&%s' % (url, self._enc_params)
|
||||
else:
|
||||
return '%s?%s' % (url, self._enc_params)
|
||||
else:
|
||||
return url
|
||||
|
||||
@property
|
||||
def path_url(self):
|
||||
"""Build the path URL to use."""
|
||||
|
||||
url = []
|
||||
|
||||
p = urlsplit(self.full_url)
|
||||
|
||||
# Proxies use full URLs.
|
||||
if p.scheme in self.proxies:
|
||||
return self.full_url
|
||||
|
||||
path = p.path
|
||||
if not path:
|
||||
path = '/'
|
||||
url.append(path)
|
||||
|
||||
query = p.query
|
||||
if query:
|
||||
url.append('?')
|
||||
url.append(query)
|
||||
|
||||
return ''.join(url)
|
||||
|
||||
|
||||
|
||||
def send(self, anyway=False, prefetch=False):
|
||||
"""Sends the request. Returns True of successful, false if not.
|
||||
If there was an HTTPError during transmission,
|
||||
self.response.status_code will contain the HTTPError code.
|
||||
|
||||
Once a request is successfully sent, `sent` will equal True.
|
||||
|
||||
:param anyway: If True, request will be sent, even if it has
|
||||
already been sent.
|
||||
"""
|
||||
|
||||
# Build the URL
|
||||
url = self.full_url
|
||||
|
||||
# Logging
|
||||
if self.config.get('verbose'):
|
||||
self.config.get('verbose').write('%s %s %s\n' % (
|
||||
datetime.now().isoformat(), self.method, url
|
||||
))
|
||||
|
||||
# Nottin' on you.
|
||||
body = None
|
||||
content_type = None
|
||||
|
||||
# Multi-part file uploads.
|
||||
if self.files:
|
||||
if not isinstance(self.data, basestring):
|
||||
|
||||
try:
|
||||
fields = self.data.copy()
|
||||
except AttributeError:
|
||||
fields = dict(self.data)
|
||||
|
||||
for (k, v) in self.files.items():
|
||||
# support for explicit filename
|
||||
if isinstance(v, (tuple, list)):
|
||||
fn, fp = v
|
||||
else:
|
||||
fn = guess_filename(v) or k
|
||||
fp = v
|
||||
fields.update({k: (fn, fp.read())})
|
||||
|
||||
(body, content_type) = encode_multipart_formdata(fields)
|
||||
else:
|
||||
pass
|
||||
# TODO: Conflict?
|
||||
else:
|
||||
if self.data:
|
||||
|
||||
body = self._enc_data
|
||||
if isinstance(self.data, basestring):
|
||||
content_type = None
|
||||
else:
|
||||
content_type = 'application/x-www-form-urlencoded'
|
||||
|
||||
# Add content-type if it wasn't explicitly provided.
|
||||
if (content_type) and (not 'content-type' in self.headers):
|
||||
self.headers['Content-Type'] = content_type
|
||||
|
||||
if self.auth:
|
||||
if isinstance(self.auth, tuple) and len(self.auth) == 2:
|
||||
# special-case basic HTTP auth
|
||||
self.auth = HTTPBasicAuth(*self.auth)
|
||||
|
||||
# Allow auth to make its changes.
|
||||
r = self.auth(self)
|
||||
|
||||
# Update self to reflect the auth changes.
|
||||
self.__dict__.update(r.__dict__)
|
||||
|
||||
_p = urlparse(url)
|
||||
proxy = self.proxies.get(_p.scheme)
|
||||
|
||||
if proxy:
|
||||
conn = poolmanager.proxy_from_url(proxy)
|
||||
_proxy = urlparse(proxy)
|
||||
if '@' in _proxy.netloc:
|
||||
auth, url = _proxy.netloc.split('@', 1)
|
||||
self.proxy_auth = HTTPProxyAuth(*auth.split(':', 1))
|
||||
r = self.proxy_auth(self)
|
||||
self.__dict__.update(r.__dict__)
|
||||
else:
|
||||
# Check to see if keep_alive is allowed.
|
||||
if self.config.get('keep_alive'):
|
||||
conn = self._poolmanager.connection_from_url(url)
|
||||
else:
|
||||
conn = connectionpool.connection_from_url(url)
|
||||
|
||||
if url.startswith('https') and self.verify:
|
||||
|
||||
cert_loc = None
|
||||
|
||||
# Allow self-specified cert location.
|
||||
if self.verify is not True:
|
||||
cert_loc = self.verify
|
||||
|
||||
|
||||
# Look for configuration.
|
||||
if not cert_loc:
|
||||
cert_loc = os.environ.get('REQUESTS_CA_BUNDLE')
|
||||
|
||||
# Curl compatiblity.
|
||||
if not cert_loc:
|
||||
cert_loc = os.environ.get('CURL_CA_BUNDLE')
|
||||
|
||||
# Use the awesome certifi list.
|
||||
if not cert_loc:
|
||||
cert_loc = __import__('certifi').where()
|
||||
|
||||
conn.cert_reqs = 'CERT_REQUIRED'
|
||||
conn.ca_certs = cert_loc
|
||||
|
||||
if not self.sent or anyway:
|
||||
|
||||
if self.cookies:
|
||||
|
||||
# Skip if 'cookie' header is explicitly set.
|
||||
if 'cookie' not in self.headers:
|
||||
|
||||
# Simple cookie with our dict.
|
||||
c = oreos.monkeys.SimpleCookie()
|
||||
for (k, v) in self.cookies.items():
|
||||
c[k] = v
|
||||
|
||||
# Turn it into a header.
|
||||
cookie_header = c.output(header='', sep='; ').strip()
|
||||
|
||||
# Attach Cookie header to request.
|
||||
self.headers['Cookie'] = cookie_header
|
||||
|
||||
try:
|
||||
# The inner try .. except re-raises certain exceptions as
|
||||
# internal exception types; the outer suppresses exceptions
|
||||
# when safe mode is set.
|
||||
try:
|
||||
# Send the request.
|
||||
r = conn.urlopen(
|
||||
method=self.method,
|
||||
url=self.path_url,
|
||||
body=body,
|
||||
headers=self.headers,
|
||||
redirect=False,
|
||||
assert_same_host=False,
|
||||
preload_content=False,
|
||||
decode_content=True,
|
||||
retries=self.config.get('max_retries', 0),
|
||||
timeout=self.timeout,
|
||||
)
|
||||
self.sent = True
|
||||
|
||||
except MaxRetryError, e:
|
||||
raise ConnectionError(e)
|
||||
|
||||
except (_SSLError, _HTTPError), e:
|
||||
if self.verify and isinstance(e, _SSLError):
|
||||
raise SSLError(e)
|
||||
|
||||
raise Timeout('Request timed out.')
|
||||
|
||||
except RequestException, e:
|
||||
if self.config.get('safe_mode', False):
|
||||
# In safe mode, catch the exception and attach it to
|
||||
# a blank urllib3.HTTPResponse object.
|
||||
r = HTTPResponse()
|
||||
r.error = e
|
||||
else:
|
||||
raise
|
||||
|
||||
self._build_response(r)
|
||||
|
||||
# Response manipulation hook.
|
||||
self.response = dispatch_hook('response', self.hooks, self.response)
|
||||
|
||||
# Post-request hook.
|
||||
r = dispatch_hook('post_request', self.hooks, self)
|
||||
self.__dict__.update(r.__dict__)
|
||||
|
||||
# If prefetch is True, mark content as consumed.
|
||||
if prefetch:
|
||||
# Save the response.
|
||||
self.response.content
|
||||
|
||||
if self.config.get('danger_mode'):
|
||||
self.response.raise_for_status()
|
||||
|
||||
return self.sent
|
||||
|
||||
|
||||
class Response(object):
|
||||
"""The core :class:`Response <Response>` object. All
|
||||
:class:`Request <Request>` objects contain a
|
||||
:class:`response <Response>` attribute, which is an instance
|
||||
of this class.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self._content = None
|
||||
self._content_consumed = False
|
||||
|
||||
#: Integer Code of responded HTTP Status.
|
||||
self.status_code = None
|
||||
|
||||
#: Case-insensitive Dictionary of Response Headers.
|
||||
#: For example, ``headers['content-encoding']`` will return the
|
||||
#: value of a ``'Content-Encoding'`` response header.
|
||||
self.headers = CaseInsensitiveDict()
|
||||
|
||||
#: File-like object representation of response (for advanced usage).
|
||||
self.raw = None
|
||||
|
||||
#: Final URL location of Response.
|
||||
self.url = None
|
||||
|
||||
#: Resulting :class:`HTTPError` of request, if one occurred.
|
||||
self.error = None
|
||||
|
||||
#: Encoding to decode with when accessing r.content.
|
||||
self.encoding = None
|
||||
|
||||
#: A list of :class:`Response <Response>` objects from
|
||||
#: the history of the Request. Any redirect responses will end
|
||||
#: up here.
|
||||
self.history = []
|
||||
|
||||
#: The :class:`Request <Request>` that created the Response.
|
||||
self.request = None
|
||||
|
||||
#: A dictionary of Cookies the server sent back.
|
||||
self.cookies = {}
|
||||
|
||||
#: Dictionary of configurations for this request.
|
||||
self.config = {}
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return '<Response [%s]>' % (self.status_code)
|
||||
|
||||
def __nonzero__(self):
|
||||
"""Returns true if :attr:`status_code` is 'OK'."""
|
||||
return self.ok
|
||||
|
||||
@property
|
||||
def ok(self):
|
||||
try:
|
||||
self.raise_for_status()
|
||||
except HTTPError:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def iter_content(self, chunk_size=10 * 1024, decode_unicode=None):
|
||||
"""Iterates over the response data. This avoids reading the content
|
||||
at once into memory for large responses. The chunk size is the number
|
||||
of bytes it should read into memory. This is not necessarily the
|
||||
length of each item returned as decoding can take place.
|
||||
"""
|
||||
if self._content_consumed:
|
||||
raise RuntimeError(
|
||||
'The content for this response was already consumed'
|
||||
)
|
||||
|
||||
def generate():
|
||||
while 1:
|
||||
chunk = self.raw.read(chunk_size)
|
||||
if not chunk:
|
||||
break
|
||||
yield chunk
|
||||
self._content_consumed = True
|
||||
|
||||
def generate_chunked():
|
||||
resp = self.raw._original_response
|
||||
fp = resp.fp
|
||||
if resp.chunk_left is not None:
|
||||
pending_bytes = resp.chunk_left
|
||||
while pending_bytes:
|
||||
chunk = fp.read(min(chunk_size, pending_bytes))
|
||||
pending_bytes-=len(chunk)
|
||||
yield chunk
|
||||
fp.read(2) # throw away crlf
|
||||
while 1:
|
||||
#XXX correct line size? (httplib has 64kb, seems insane)
|
||||
pending_bytes = fp.readline(40).strip()
|
||||
pending_bytes = int(pending_bytes, 16)
|
||||
if pending_bytes == 0:
|
||||
break
|
||||
while pending_bytes:
|
||||
chunk = fp.read(min(chunk_size, pending_bytes))
|
||||
pending_bytes-=len(chunk)
|
||||
yield chunk
|
||||
fp.read(2) # throw away crlf
|
||||
self._content_consumed = True
|
||||
fp.close()
|
||||
|
||||
|
||||
if getattr(getattr(self.raw, '_original_response', None), 'chunked', False):
|
||||
gen = generate_chunked()
|
||||
else:
|
||||
gen = generate()
|
||||
|
||||
if 'gzip' in self.headers.get('content-encoding', ''):
|
||||
gen = stream_decompress(gen, mode='gzip')
|
||||
elif 'deflate' in self.headers.get('content-encoding', ''):
|
||||
gen = stream_decompress(gen, mode='deflate')
|
||||
|
||||
if decode_unicode is None:
|
||||
decode_unicode = self.config.get('decode_unicode')
|
||||
|
||||
if decode_unicode:
|
||||
gen = stream_decode_response_unicode(gen, self)
|
||||
|
||||
return gen
|
||||
|
||||
|
||||
def iter_lines(self, chunk_size=10 * 1024, decode_unicode=None):
|
||||
"""Iterates over the response data, one line at a time. This
|
||||
avoids reading the content at once into memory for large
|
||||
responses.
|
||||
"""
|
||||
#XXX: why rstrip by default
|
||||
pending = None
|
||||
for chunk in self.iter_content(chunk_size, decode_unicode=decode_unicode):
|
||||
if pending is not None:
|
||||
chunk = pending + chunk
|
||||
lines = chunk.splitlines(True)
|
||||
for line in lines[:-1]:
|
||||
yield line.rstrip()
|
||||
# Save the last part of the chunk for next iteration, to keep full line together
|
||||
# lines may be empty for the last chunk of a chunked response
|
||||
if lines:
|
||||
pending = lines[-1]
|
||||
#if pending is a complete line, give it baack
|
||||
if pending[-1] == '\n':
|
||||
yield pending.rstrip()
|
||||
pending = None
|
||||
else:
|
||||
pending = None
|
||||
|
||||
# Yield the last line
|
||||
if pending is not None:
|
||||
yield pending.rstrip()
|
||||
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
"""Content of the response, in bytes or unicode
|
||||
(if available).
|
||||
"""
|
||||
|
||||
if self._content is None:
|
||||
# Read the contents.
|
||||
try:
|
||||
if self._content_consumed:
|
||||
raise RuntimeError(
|
||||
'The content for this response was already consumed')
|
||||
|
||||
self._content = self.raw.read()
|
||||
except AttributeError:
|
||||
self._content = None
|
||||
|
||||
content = self._content
|
||||
|
||||
# Decode unicode content.
|
||||
if self.config.get('decode_unicode'):
|
||||
|
||||
# Try charset from content-type
|
||||
|
||||
if self.encoding:
|
||||
try:
|
||||
content = unicode(content, self.encoding)
|
||||
except UnicodeError:
|
||||
pass
|
||||
|
||||
# Fall back:
|
||||
try:
|
||||
content = unicode(content, self.encoding, errors='replace')
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
self._content_consumed = True
|
||||
return content
|
||||
|
||||
|
||||
def raise_for_status(self):
|
||||
"""Raises stored :class:`HTTPError` or :class:`URLError`, if one occurred."""
|
||||
|
||||
if self.error:
|
||||
raise self.error
|
||||
|
||||
if (self.status_code >= 300) and (self.status_code < 400):
|
||||
raise HTTPError('%s Redirection' % self.status_code)
|
||||
|
||||
elif (self.status_code >= 400) and (self.status_code < 500):
|
||||
raise HTTPError('%s Client Error' % self.status_code)
|
||||
|
||||
elif (self.status_code >= 500) and (self.status_code < 600):
|
||||
raise HTTPError('%s Server Error' % self.status_code)
|
||||
|
||||
|
||||
3
requests/packages/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from . import urllib3
|
||||
3
requests/packages/oreos/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .core import dict_from_string
|
||||
24
requests/packages/oreos/core.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
oreos.core
|
||||
~~~~~~~~~~
|
||||
|
||||
The creamy white center.
|
||||
"""
|
||||
|
||||
from .monkeys import SimpleCookie
|
||||
|
||||
|
||||
def dict_from_string(s):
|
||||
"""Returns a MultiDict with Cookies."""
|
||||
|
||||
cookies = dict()
|
||||
|
||||
c = SimpleCookie()
|
||||
c.load(s)
|
||||
|
||||
for k,v in c.items():
|
||||
cookies.update({k: v.value})
|
||||
|
||||
return cookies
|
||||
770
requests/packages/oreos/monkeys.py
Normal file
@@ -0,0 +1,770 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
oreos.monkeys
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Monkeypatches.
|
||||
"""
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
|
||||
####
|
||||
# Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software
|
||||
# and its documentation for any purpose and without fee is hereby
|
||||
# granted, provided that the above copyright notice appear in all
|
||||
# copies and that both that copyright notice and this permission
|
||||
# notice appear in supporting documentation, and that the name of
|
||||
# Timothy O'Malley not be used in advertising or publicity
|
||||
# pertaining to distribution of the software without specific, written
|
||||
# prior permission.
|
||||
#
|
||||
# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
||||
# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
|
||||
# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
# PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
####
|
||||
#
|
||||
# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
|
||||
# by Timothy O'Malley <timo@alum.mit.edu>
|
||||
#
|
||||
# Cookie.py is a Python module for the handling of HTTP
|
||||
# cookies as a Python dictionary. See RFC 2109 for more
|
||||
# information on cookies.
|
||||
#
|
||||
# The original idea to treat Cookies as a dictionary came from
|
||||
# Dave Mitchell (davem@magnet.com) in 1995, when he released the
|
||||
# first version of nscookie.py.
|
||||
#
|
||||
####
|
||||
|
||||
r"""
|
||||
Here's a sample session to show how to use this module.
|
||||
At the moment, this is the only documentation.
|
||||
|
||||
The Basics
|
||||
----------
|
||||
|
||||
Importing is easy..
|
||||
|
||||
>>> import Cookie
|
||||
|
||||
Most of the time you start by creating a cookie. Cookies come in
|
||||
three flavors, each with slightly different encoding semantics, but
|
||||
more on that later.
|
||||
|
||||
>>> C = Cookie.SimpleCookie()
|
||||
>>> C = Cookie.SerialCookie()
|
||||
>>> C = Cookie.SmartCookie()
|
||||
|
||||
[Note: Long-time users of Cookie.py will remember using
|
||||
Cookie.Cookie() to create an Cookie object. Although deprecated, it
|
||||
is still supported by the code. See the Backward Compatibility notes
|
||||
for more information.]
|
||||
|
||||
Once you've created your Cookie, you can add values just as if it were
|
||||
a dictionary.
|
||||
|
||||
>>> C = Cookie.SmartCookie()
|
||||
>>> C["fig"] = "newton"
|
||||
>>> C["sugar"] = "wafer"
|
||||
>>> C.output()
|
||||
'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
|
||||
|
||||
Notice that the printable representation of a Cookie is the
|
||||
appropriate format for a Set-Cookie: header. This is the
|
||||
default behavior. You can change the header and printed
|
||||
attributes by using the .output() function
|
||||
|
||||
>>> C = Cookie.SmartCookie()
|
||||
>>> C["rocky"] = "road"
|
||||
>>> C["rocky"]["path"] = "/cookie"
|
||||
>>> print C.output(header="Cookie:")
|
||||
Cookie: rocky=road; Path=/cookie
|
||||
>>> print C.output(attrs=[], header="Cookie:")
|
||||
Cookie: rocky=road
|
||||
|
||||
The load() method of a Cookie extracts cookies from a string. In a
|
||||
CGI script, you would use this method to extract the cookies from the
|
||||
HTTP_COOKIE environment variable.
|
||||
|
||||
>>> C = Cookie.SmartCookie()
|
||||
>>> C.load("chips=ahoy; vienna=finger")
|
||||
>>> C.output()
|
||||
'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
|
||||
|
||||
The load() method is darn-tootin smart about identifying cookies
|
||||
within a string. Escaped quotation marks, nested semicolons, and other
|
||||
such trickeries do not confuse it.
|
||||
|
||||
>>> C = Cookie.SmartCookie()
|
||||
>>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
|
||||
>>> print C
|
||||
Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
|
||||
|
||||
Each element of the Cookie also supports all of the RFC 2109
|
||||
Cookie attributes. Here's an example which sets the Path
|
||||
attribute.
|
||||
|
||||
>>> C = Cookie.SmartCookie()
|
||||
>>> C["oreo"] = "doublestuff"
|
||||
>>> C["oreo"]["path"] = "/"
|
||||
>>> print C
|
||||
Set-Cookie: oreo=doublestuff; Path=/
|
||||
|
||||
Each dictionary element has a 'value' attribute, which gives you
|
||||
back the value associated with the key.
|
||||
|
||||
>>> C = Cookie.SmartCookie()
|
||||
>>> C["twix"] = "none for you"
|
||||
>>> C["twix"].value
|
||||
'none for you'
|
||||
|
||||
|
||||
A Bit More Advanced
|
||||
-------------------
|
||||
|
||||
As mentioned before, there are three different flavors of Cookie
|
||||
objects, each with different encoding/decoding semantics. This
|
||||
section briefly discusses the differences.
|
||||
|
||||
SimpleCookie
|
||||
|
||||
The SimpleCookie expects that all values should be standard strings.
|
||||
Just to be sure, SimpleCookie invokes the str() builtin to convert
|
||||
the value to a string, when the values are set dictionary-style.
|
||||
|
||||
>>> C = Cookie.SimpleCookie()
|
||||
>>> C["number"] = 7
|
||||
>>> C["string"] = "seven"
|
||||
>>> C["number"].value
|
||||
'7'
|
||||
>>> C["string"].value
|
||||
'seven'
|
||||
>>> C.output()
|
||||
'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
|
||||
|
||||
|
||||
SerialCookie
|
||||
|
||||
The SerialCookie expects that all values should be serialized using
|
||||
cPickle (or pickle, if cPickle isn't available). As a result of
|
||||
serializing, SerialCookie can save almost any Python object to a
|
||||
value, and recover the exact same object when the cookie has been
|
||||
returned. (SerialCookie can yield some strange-looking cookie
|
||||
values, however.)
|
||||
|
||||
>>> C = Cookie.SerialCookie()
|
||||
>>> C["number"] = 7
|
||||
>>> C["string"] = "seven"
|
||||
>>> C["number"].value
|
||||
7
|
||||
>>> C["string"].value
|
||||
'seven'
|
||||
>>> C.output()
|
||||
'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string="S\'seven\'\\012p1\\012."'
|
||||
|
||||
Be warned, however, if SerialCookie cannot de-serialize a value (because
|
||||
it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
|
||||
|
||||
|
||||
SmartCookie
|
||||
|
||||
The SmartCookie combines aspects of each of the other two flavors.
|
||||
When setting a value in a dictionary-fashion, the SmartCookie will
|
||||
serialize (ala cPickle) the value *if and only if* it isn't a
|
||||
Python string. String objects are *not* serialized. Similarly,
|
||||
when the load() method parses out values, it attempts to de-serialize
|
||||
the value. If it fails, then it fallsback to treating the value
|
||||
as a string.
|
||||
|
||||
>>> C = Cookie.SmartCookie()
|
||||
>>> C["number"] = 7
|
||||
>>> C["string"] = "seven"
|
||||
>>> C["number"].value
|
||||
7
|
||||
>>> C["string"].value
|
||||
'seven'
|
||||
>>> C.output()
|
||||
'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string=seven'
|
||||
|
||||
|
||||
Backwards Compatibility
|
||||
-----------------------
|
||||
|
||||
In order to keep compatibilty with earlier versions of Cookie.py,
|
||||
it is still possible to use Cookie.Cookie() to create a Cookie. In
|
||||
fact, this simply returns a SmartCookie.
|
||||
|
||||
>>> C = Cookie.Cookie()
|
||||
>>> print C.__class__.__name__
|
||||
SmartCookie
|
||||
|
||||
|
||||
Finis.
|
||||
""" #"
|
||||
# ^
|
||||
# |----helps out font-lock
|
||||
|
||||
#
|
||||
# Import our required modules
|
||||
#
|
||||
import string
|
||||
|
||||
try:
|
||||
from cPickle import dumps, loads
|
||||
except ImportError:
|
||||
from pickle import dumps, loads
|
||||
|
||||
import re, warnings
|
||||
|
||||
__all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie",
|
||||
"SmartCookie","Cookie"]
|
||||
|
||||
_nulljoin = ''.join
|
||||
_semispacejoin = '; '.join
|
||||
_spacejoin = ' '.join
|
||||
|
||||
#
|
||||
# Define an exception visible to External modules
|
||||
#
|
||||
class CookieError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# These quoting routines conform to the RFC2109 specification, which in
|
||||
# turn references the character definitions from RFC2068. They provide
|
||||
# a two-way quoting algorithm. Any non-text character is translated
|
||||
# into a 4 character sequence: a forward-slash followed by the
|
||||
# three-digit octal equivalent of the character. Any '\' or '"' is
|
||||
# quoted with a preceeding '\' slash.
|
||||
#
|
||||
# These are taken from RFC2068 and RFC2109.
|
||||
# _LegalChars is the list of chars which don't require "'s
|
||||
# _Translator hash-table for fast quoting
|
||||
#
|
||||
_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~[]_"
|
||||
_Translator = {
|
||||
'\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
|
||||
'\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
|
||||
'\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
|
||||
'\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
|
||||
'\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
|
||||
'\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
|
||||
'\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
|
||||
'\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
|
||||
'\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
|
||||
'\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
|
||||
'\036' : '\\036', '\037' : '\\037',
|
||||
|
||||
# Because of the way browsers really handle cookies (as opposed
|
||||
# to what the RFC says) we also encode , and ;
|
||||
|
||||
',' : '\\054', ';' : '\\073',
|
||||
|
||||
'"' : '\\"', '\\' : '\\\\',
|
||||
|
||||
'\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
|
||||
'\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
|
||||
'\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
|
||||
'\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
|
||||
'\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
|
||||
'\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
|
||||
'\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
|
||||
'\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
|
||||
'\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
|
||||
'\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
|
||||
'\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
|
||||
'\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
|
||||
'\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
|
||||
'\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
|
||||
'\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
|
||||
'\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
|
||||
'\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
|
||||
'\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
|
||||
'\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
|
||||
'\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
|
||||
'\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
|
||||
'\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
|
||||
'\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
|
||||
'\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
|
||||
'\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
|
||||
'\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
|
||||
'\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
|
||||
'\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
|
||||
'\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
|
||||
'\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
|
||||
'\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
|
||||
'\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
|
||||
'\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
|
||||
'\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
|
||||
'\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
|
||||
'\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
|
||||
'\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
|
||||
'\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
|
||||
'\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
|
||||
'\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
|
||||
'\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
|
||||
'\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
|
||||
'\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
|
||||
}
|
||||
|
||||
_idmap = ''.join(chr(x) for x in xrange(256))
|
||||
|
||||
def _quote(str, LegalChars=_LegalChars,
|
||||
idmap=_idmap, translate=string.translate):
|
||||
#
|
||||
# If the string does not need to be double-quoted,
|
||||
# then just return the string. Otherwise, surround
|
||||
# the string in doublequotes and precede quote (with a \)
|
||||
# special characters.
|
||||
#
|
||||
if "" == translate(str, idmap, LegalChars):
|
||||
return str
|
||||
else:
|
||||
return '"' + _nulljoin( map(_Translator.get, str, str) ) + '"'
|
||||
# end _quote
|
||||
|
||||
|
||||
_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
|
||||
_QuotePatt = re.compile(r"[\\].")
|
||||
|
||||
def _unquote(str):
|
||||
# If there aren't any doublequotes,
|
||||
# then there can't be any special characters. See RFC 2109.
|
||||
if len(str) < 2:
|
||||
return str
|
||||
if str[0] != '"' or str[-1] != '"':
|
||||
return str
|
||||
|
||||
# We have to assume that we must decode this string.
|
||||
# Down to work.
|
||||
|
||||
# Remove the "s
|
||||
str = str[1:-1]
|
||||
|
||||
# Check for special sequences. Examples:
|
||||
# \012 --> \n
|
||||
# \" --> "
|
||||
#
|
||||
i = 0
|
||||
n = len(str)
|
||||
res = []
|
||||
while 0 <= i < n:
|
||||
Omatch = _OctalPatt.search(str, i)
|
||||
Qmatch = _QuotePatt.search(str, i)
|
||||
if not Omatch and not Qmatch: # Neither matched
|
||||
res.append(str[i:])
|
||||
break
|
||||
# else:
|
||||
j = k = -1
|
||||
if Omatch: j = Omatch.start(0)
|
||||
if Qmatch: k = Qmatch.start(0)
|
||||
if Qmatch and ( not Omatch or k < j ): # QuotePatt matched
|
||||
res.append(str[i:k])
|
||||
res.append(str[k+1])
|
||||
i = k+2
|
||||
else: # OctalPatt matched
|
||||
res.append(str[i:j])
|
||||
res.append( chr( int(str[j+1:j+4], 8) ) )
|
||||
i = j+4
|
||||
return _nulljoin(res)
|
||||
# end _unquote
|
||||
|
||||
# The _getdate() routine is used to set the expiration time in
|
||||
# the cookie's HTTP header. By default, _getdate() returns the
|
||||
# current time in the appropriate "expires" format for a
|
||||
# Set-Cookie header. The one optional argument is an offset from
|
||||
# now, in seconds. For example, an offset of -3600 means "one hour ago".
|
||||
# The offset may be a floating point number.
|
||||
#
|
||||
|
||||
_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||
|
||||
_monthname = [None,
|
||||
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||
|
||||
def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
|
||||
from time import gmtime, time
|
||||
now = time()
|
||||
year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
|
||||
return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
|
||||
(weekdayname[wd], day, monthname[month], year, hh, mm, ss)
|
||||
|
||||
|
||||
#
|
||||
# A class to hold ONE key,value pair.
|
||||
# In a cookie, each such pair may have several attributes.
|
||||
# so this class is used to keep the attributes associated
|
||||
# with the appropriate key,value pair.
|
||||
# This class also includes a coded_value attribute, which
|
||||
# is used to hold the network representation of the
|
||||
# value. This is most useful when Python objects are
|
||||
# pickled for network transit.
|
||||
#
|
||||
|
||||
class Morsel(dict):
|
||||
# RFC 2109 lists these attributes as reserved:
|
||||
# path comment domain
|
||||
# max-age secure version
|
||||
#
|
||||
# For historical reasons, these attributes are also reserved:
|
||||
# expires
|
||||
#
|
||||
# This is an extension from Microsoft:
|
||||
# httponly
|
||||
#
|
||||
# This dictionary provides a mapping from the lowercase
|
||||
# variant on the left to the appropriate traditional
|
||||
# formatting on the right.
|
||||
_reserved = { "expires" : "expires",
|
||||
"path" : "Path",
|
||||
"comment" : "Comment",
|
||||
"domain" : "Domain",
|
||||
"max-age" : "Max-Age",
|
||||
"secure" : "secure",
|
||||
"httponly" : "httponly",
|
||||
"version" : "Version",
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
# Set defaults
|
||||
self.key = self.value = self.coded_value = None
|
||||
|
||||
# Set default attributes
|
||||
for K in self._reserved:
|
||||
dict.__setitem__(self, K, "")
|
||||
# end __init__
|
||||
|
||||
def __setitem__(self, K, V):
|
||||
K = K.lower()
|
||||
if not K in self._reserved:
|
||||
raise CookieError("Invalid Attribute %s" % K)
|
||||
dict.__setitem__(self, K, V)
|
||||
# end __setitem__
|
||||
|
||||
def isReservedKey(self, K):
|
||||
return K.lower() in self._reserved
|
||||
# end isReservedKey
|
||||
|
||||
def set(self, key, val, coded_val,
|
||||
LegalChars=_LegalChars,
|
||||
idmap=_idmap, translate=string.translate):
|
||||
# First we verify that the key isn't a reserved word
|
||||
# Second we make sure it only contains legal characters
|
||||
if key.lower() in self._reserved:
|
||||
raise CookieError("Attempt to set a reserved key: %s" % key)
|
||||
if "" != translate(key, idmap, LegalChars):
|
||||
raise CookieError("Illegal key value: %s" % key)
|
||||
|
||||
# It's a good key, so save it.
|
||||
self.key = key
|
||||
self.value = val
|
||||
self.coded_value = coded_val
|
||||
# end set
|
||||
|
||||
def output(self, attrs=None, header = "Set-Cookie:"):
|
||||
return "%s %s" % ( header, self.OutputString(attrs) )
|
||||
|
||||
__str__ = output
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s: %s=%s>' % (self.__class__.__name__,
|
||||
self.key, repr(self.value) )
|
||||
|
||||
def js_output(self, attrs=None):
|
||||
# Print javascript
|
||||
return """
|
||||
<script type="text/javascript">
|
||||
<!-- begin hiding
|
||||
document.cookie = \"%s\";
|
||||
// end hiding -->
|
||||
</script>
|
||||
""" % ( self.OutputString(attrs).replace('"',r'\"'), )
|
||||
# end js_output()
|
||||
|
||||
def OutputString(self, attrs=None):
|
||||
# Build up our result
|
||||
#
|
||||
result = []
|
||||
RA = result.append
|
||||
|
||||
# First, the key=value pair
|
||||
RA("%s=%s" % (self.key, self.coded_value))
|
||||
|
||||
# Now add any defined attributes
|
||||
if attrs is None:
|
||||
attrs = self._reserved
|
||||
items = self.items()
|
||||
items.sort()
|
||||
for K,V in items:
|
||||
if V == "": continue
|
||||
if K not in attrs: continue
|
||||
if K == "expires" and type(V) == type(1):
|
||||
RA("%s=%s" % (self._reserved[K], _getdate(V)))
|
||||
elif K == "max-age" and type(V) == type(1):
|
||||
RA("%s=%d" % (self._reserved[K], V))
|
||||
elif K == "secure":
|
||||
RA(str(self._reserved[K]))
|
||||
elif K == "httponly":
|
||||
RA(str(self._reserved[K]))
|
||||
else:
|
||||
RA("%s=%s" % (self._reserved[K], V))
|
||||
|
||||
# Return the result
|
||||
return _semispacejoin(result)
|
||||
# end OutputString
|
||||
# end Morsel class
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Pattern for finding cookie
|
||||
#
|
||||
# This used to be strict parsing based on the RFC2109 and RFC2068
|
||||
# specifications. I have since discovered that MSIE 3.0x doesn't
|
||||
# follow the character rules outlined in those specs. As a
|
||||
# result, the parsing rules here are less strict.
|
||||
#
|
||||
|
||||
_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=\[\]\_]"
|
||||
|
||||
_CookiePattern = re.compile(
|
||||
r"(?x)" # This is a Verbose pattern
|
||||
r"(?P<key>" # Start of group 'key'
|
||||
""+ _LegalCharsPatt +"+?" # Any word of at least one letter, nongreedy
|
||||
r")" # End of group 'key'
|
||||
r"\s*=\s*" # Equal Sign
|
||||
r"(?P<val>" # Start of group 'val'
|
||||
r'"(?:[^\\"]|\\.)*"' # Any doublequoted string
|
||||
r"|" # or
|
||||
r"\w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr
|
||||
r"|" # or
|
||||
""+ _LegalCharsPatt +"*" # Any word or empty string
|
||||
r")" # End of group 'val'
|
||||
r"\s*;?" # Probably ending in a semi-colon
|
||||
)
|
||||
|
||||
|
||||
# At long last, here is the cookie class.
|
||||
# Using this class is almost just like using a dictionary.
|
||||
# See this module's docstring for example usage.
|
||||
#
|
||||
class BaseCookie(dict):
|
||||
# A container class for a set of Morsels
|
||||
#
|
||||
|
||||
def value_decode(self, val):
|
||||
"""real_value, coded_value = value_decode(STRING)
|
||||
Called prior to setting a cookie's value from the network
|
||||
representation. The VALUE is the value read from HTTP
|
||||
header.
|
||||
Override this function to modify the behavior of cookies.
|
||||
"""
|
||||
return val, val
|
||||
# end value_encode
|
||||
|
||||
def value_encode(self, val):
|
||||
"""real_value, coded_value = value_encode(VALUE)
|
||||
Called prior to setting a cookie's value from the dictionary
|
||||
representation. The VALUE is the value being assigned.
|
||||
Override this function to modify the behavior of cookies.
|
||||
"""
|
||||
strval = str(val)
|
||||
return strval, strval
|
||||
# end value_encode
|
||||
|
||||
def __init__(self, input=None):
|
||||
if input: self.load(input)
|
||||
# end __init__
|
||||
|
||||
def __set(self, key, real_value, coded_value):
|
||||
"""Private method for setting a cookie's value"""
|
||||
M = self.get(key, Morsel())
|
||||
M.set(key, real_value, coded_value)
|
||||
dict.__setitem__(self, key, M)
|
||||
# end __set
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Dictionary style assignment."""
|
||||
rval, cval = self.value_encode(value)
|
||||
self.__set(key, rval, cval)
|
||||
# end __setitem__
|
||||
|
||||
def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
|
||||
"""Return a string suitable for HTTP."""
|
||||
result = []
|
||||
items = self.items()
|
||||
items.sort()
|
||||
for K,V in items:
|
||||
result.append( V.output(attrs, header) )
|
||||
return sep.join(result)
|
||||
# end output
|
||||
|
||||
__str__ = output
|
||||
|
||||
def __repr__(self):
|
||||
L = []
|
||||
items = self.items()
|
||||
items.sort()
|
||||
for K,V in items:
|
||||
L.append( '%s=%s' % (K,repr(V.value) ) )
|
||||
return '<%s: %s>' % (self.__class__.__name__, _spacejoin(L))
|
||||
|
||||
def js_output(self, attrs=None):
|
||||
"""Return a string suitable for JavaScript."""
|
||||
result = []
|
||||
items = self.items()
|
||||
items.sort()
|
||||
for K,V in items:
|
||||
result.append( V.js_output(attrs) )
|
||||
return _nulljoin(result)
|
||||
# end js_output
|
||||
|
||||
def load(self, rawdata):
|
||||
"""Load cookies from a string (presumably HTTP_COOKIE) or
|
||||
from a dictionary. Loading cookies from a dictionary 'd'
|
||||
is equivalent to calling:
|
||||
map(Cookie.__setitem__, d.keys(), d.values())
|
||||
"""
|
||||
if type(rawdata) == type(""):
|
||||
self.__ParseString(rawdata)
|
||||
else:
|
||||
# self.update() wouldn't call our custom __setitem__
|
||||
for k, v in rawdata.items():
|
||||
self[k] = v
|
||||
return
|
||||
# end load()
|
||||
|
||||
def __ParseString(self, str, patt=_CookiePattern):
|
||||
i = 0 # Our starting point
|
||||
n = len(str) # Length of string
|
||||
M = None # current morsel
|
||||
|
||||
while 0 <= i < n:
|
||||
# Start looking for a cookie
|
||||
match = patt.search(str, i)
|
||||
if not match: break # No more cookies
|
||||
|
||||
K,V = match.group("key"), match.group("val")
|
||||
i = match.end(0)
|
||||
|
||||
# Parse the key, value in case it's metainfo
|
||||
if K[0] == "$":
|
||||
# We ignore attributes which pertain to the cookie
|
||||
# mechanism as a whole. See RFC 2109.
|
||||
# (Does anyone care?)
|
||||
if M:
|
||||
M[ K[1:] ] = V
|
||||
elif K.lower() in Morsel._reserved:
|
||||
if M:
|
||||
M[ K ] = _unquote(V)
|
||||
else:
|
||||
rval, cval = self.value_decode(V)
|
||||
self.__set(K, rval, cval)
|
||||
M = self[K]
|
||||
# end __ParseString
|
||||
# end BaseCookie class
|
||||
|
||||
class SimpleCookie(BaseCookie):
|
||||
"""SimpleCookie
|
||||
SimpleCookie supports strings as cookie values. When setting
|
||||
the value using the dictionary assignment notation, SimpleCookie
|
||||
calls the builtin str() to convert the value to a string. Values
|
||||
received from HTTP are kept as strings.
|
||||
"""
|
||||
def value_decode(self, val):
|
||||
return _unquote( val ), val
|
||||
def value_encode(self, val):
|
||||
strval = str(val)
|
||||
return strval, _quote( strval )
|
||||
# end SimpleCookie
|
||||
|
||||
class SerialCookie(BaseCookie):
|
||||
"""SerialCookie
|
||||
SerialCookie supports arbitrary objects as cookie values. All
|
||||
values are serialized (using cPickle) before being sent to the
|
||||
client. All incoming values are assumed to be valid Pickle
|
||||
representations. IF AN INCOMING VALUE IS NOT IN A VALID PICKLE
|
||||
FORMAT, THEN AN EXCEPTION WILL BE RAISED.
|
||||
|
||||
Note: Large cookie values add overhead because they must be
|
||||
retransmitted on every HTTP transaction.
|
||||
|
||||
Note: HTTP has a 2k limit on the size of a cookie. This class
|
||||
does not check for this limit, so be careful!!!
|
||||
"""
|
||||
def __init__(self, input=None):
|
||||
warnings.warn("SerialCookie class is insecure; do not use it",
|
||||
DeprecationWarning)
|
||||
BaseCookie.__init__(self, input)
|
||||
# end __init__
|
||||
def value_decode(self, val):
|
||||
# This could raise an exception!
|
||||
return loads( _unquote(val) ), val
|
||||
def value_encode(self, val):
|
||||
return val, _quote( dumps(val) )
|
||||
# end SerialCookie
|
||||
|
||||
class SmartCookie(BaseCookie):
|
||||
"""SmartCookie
|
||||
SmartCookie supports arbitrary objects as cookie values. If the
|
||||
object is a string, then it is quoted. If the object is not a
|
||||
string, however, then SmartCookie will use cPickle to serialize
|
||||
the object into a string representation.
|
||||
|
||||
Note: Large cookie values add overhead because they must be
|
||||
retransmitted on every HTTP transaction.
|
||||
|
||||
Note: HTTP has a 2k limit on the size of a cookie. This class
|
||||
does not check for this limit, so be careful!!!
|
||||
"""
|
||||
def __init__(self, input=None):
|
||||
warnings.warn("Cookie/SmartCookie class is insecure; do not use it",
|
||||
DeprecationWarning)
|
||||
BaseCookie.__init__(self, input)
|
||||
# end __init__
|
||||
def value_decode(self, val):
|
||||
strval = _unquote(val)
|
||||
try:
|
||||
return loads(strval), val
|
||||
except:
|
||||
return strval, val
|
||||
def value_encode(self, val):
|
||||
if type(val) == type(""):
|
||||
return val, _quote(val)
|
||||
else:
|
||||
return val, _quote( dumps(val) )
|
||||
# end SmartCookie
|
||||
|
||||
|
||||
###########################################################
|
||||
# Backwards Compatibility: Don't break any existing code!
|
||||
|
||||
# We provide Cookie() as an alias for SmartCookie()
|
||||
Cookie = SmartCookie
|
||||
|
||||
#
|
||||
###########################################################
|
||||
|
||||
def _test():
|
||||
import doctest, Cookie
|
||||
return doctest.testmod(Cookie)
|
||||
|
||||
if __name__ == "__main__":
|
||||
_test()
|
||||
|
||||
|
||||
#Local Variables:
|
||||
#tab-width: 4
|
||||
#end:
|
||||
399
requests/packages/oreos/structures.py
Normal file
@@ -0,0 +1,399 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
oreos.sructures
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The plastic blue packaging.
|
||||
|
||||
This is mostly directly stolen from mitsuhiko/werkzeug.
|
||||
"""
|
||||
|
||||
__all__ = ('MultiDict',)
|
||||
|
||||
class _Missing(object):
|
||||
|
||||
def __repr__(self):
|
||||
return 'no value'
|
||||
|
||||
def __reduce__(self):
|
||||
return '_missing'
|
||||
|
||||
_missing = _Missing()
|
||||
|
||||
|
||||
|
||||
def iter_multi_items(mapping):
|
||||
"""Iterates over the items of a mapping yielding keys and values
|
||||
without dropping any from more complex structures.
|
||||
"""
|
||||
if isinstance(mapping, MultiDict):
|
||||
for item in mapping.iteritems(multi=True):
|
||||
yield item
|
||||
elif isinstance(mapping, dict):
|
||||
for key, value in mapping.iteritems():
|
||||
if isinstance(value, (tuple, list)):
|
||||
for value in value:
|
||||
yield key, value
|
||||
else:
|
||||
yield key, value
|
||||
else:
|
||||
for item in mapping:
|
||||
yield item
|
||||
|
||||
|
||||
|
||||
class TypeConversionDict(dict):
|
||||
"""Works like a regular dict but the :meth:`get` method can perform
|
||||
type conversions. :class:`MultiDict` and :class:`CombinedMultiDict`
|
||||
are subclasses of this class and provide the same feature.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
|
||||
def get(self, key, default=None, type=None):
|
||||
"""Return the default value if the requested data doesn't exist.
|
||||
If `type` is provided and is a callable it should convert the value,
|
||||
return it or raise a :exc:`ValueError` if that is not possible. In
|
||||
this case the function will return the default as if the value was not
|
||||
found:
|
||||
|
||||
>>> d = TypeConversionDict(foo='42', bar='blub')
|
||||
>>> d.get('foo', type=int)
|
||||
42
|
||||
>>> d.get('bar', -1, type=int)
|
||||
-1
|
||||
|
||||
:param key: The key to be looked up.
|
||||
:param default: The default value to be returned if the key can't
|
||||
be looked up. If not further specified `None` is
|
||||
returned.
|
||||
:param type: A callable that is used to cast the value in the
|
||||
:class:`MultiDict`. If a :exc:`ValueError` is raised
|
||||
by this callable the default value is returned.
|
||||
"""
|
||||
try:
|
||||
rv = self[key]
|
||||
if type is not None:
|
||||
rv = type(rv)
|
||||
except (KeyError, ValueError):
|
||||
rv = default
|
||||
return rv
|
||||
|
||||
|
||||
class MultiDict(TypeConversionDict):
|
||||
"""A :class:`MultiDict` is a dictionary subclass customized to deal with
|
||||
multiple values for the same key which is for example used by the parsing
|
||||
functions in the wrappers. This is necessary because some HTML form
|
||||
elements pass multiple values for the same key.
|
||||
|
||||
:class:`MultiDict` implements all standard dictionary methods.
|
||||
Internally, it saves all values for a key as a list, but the standard dict
|
||||
access methods will only return the first value for a key. If you want to
|
||||
gain access to the other values, too, you have to use the `list` methods as
|
||||
explained below.
|
||||
|
||||
Basic Usage:
|
||||
|
||||
>>> d = MultiDict([('a', 'b'), ('a', 'c')])
|
||||
>>> d
|
||||
MultiDict([('a', 'b'), ('a', 'c')])
|
||||
>>> d['a']
|
||||
'b'
|
||||
>>> d.getlist('a')
|
||||
['b', 'c']
|
||||
>>> 'a' in d
|
||||
True
|
||||
|
||||
It behaves like a normal dict thus all dict functions will only return the
|
||||
first value when multiple values for one key are found.
|
||||
|
||||
From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a
|
||||
subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will
|
||||
render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP
|
||||
exceptions.
|
||||
|
||||
A :class:`MultiDict` can be constructed from an iterable of
|
||||
``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2
|
||||
onwards some keyword parameters.
|
||||
|
||||
:param mapping: the initial value for the :class:`MultiDict`. Either a
|
||||
regular dict, an iterable of ``(key, value)`` tuples
|
||||
or `None`.
|
||||
"""
|
||||
|
||||
def __init__(self, mapping=None):
|
||||
if isinstance(mapping, MultiDict):
|
||||
dict.__init__(self, ((k, l[:]) for k, l in mapping.iterlists()))
|
||||
elif isinstance(mapping, dict):
|
||||
tmp = {}
|
||||
for key, value in mapping.iteritems():
|
||||
if isinstance(value, (tuple, list)):
|
||||
value = list(value)
|
||||
else:
|
||||
value = [value]
|
||||
tmp[key] = value
|
||||
dict.__init__(self, tmp)
|
||||
else:
|
||||
tmp = {}
|
||||
for key, value in mapping or ():
|
||||
tmp.setdefault(key, []).append(value)
|
||||
dict.__init__(self, tmp)
|
||||
|
||||
def __getstate__(self):
|
||||
return dict(self.lists())
|
||||
|
||||
def __setstate__(self, value):
|
||||
dict.clear(self)
|
||||
dict.update(self, value)
|
||||
|
||||
def __iter__(self):
|
||||
return self.iterkeys()
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Return the first data value for this key;
|
||||
raises KeyError if not found.
|
||||
|
||||
:param key: The key to be looked up.
|
||||
:raise KeyError: if the key does not exist.
|
||||
"""
|
||||
if key in self:
|
||||
return dict.__getitem__(self, key)[0]
|
||||
raise KeyError(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""Like :meth:`add` but removes an existing key first.
|
||||
|
||||
:param key: the key for the value.
|
||||
:param value: the value to set.
|
||||
"""
|
||||
dict.__setitem__(self, key, [value])
|
||||
|
||||
def add(self, key, value):
|
||||
"""Adds a new value for the key.
|
||||
|
||||
.. versionadded:: 0.6
|
||||
|
||||
:param key: the key for the value.
|
||||
:param value: the value to add.
|
||||
"""
|
||||
dict.setdefault(self, key, []).append(value)
|
||||
|
||||
def getlist(self, key, type=None):
|
||||
"""Return the list of items for a given key. If that key is not in the
|
||||
`MultiDict`, the return value will be an empty list. Just as `get`
|
||||
`getlist` accepts a `type` parameter. All items will be converted
|
||||
with the callable defined there.
|
||||
|
||||
:param key: The key to be looked up.
|
||||
:param type: A callable that is used to cast the value in the
|
||||
:class:`MultiDict`. If a :exc:`ValueError` is raised
|
||||
by this callable the value will be removed from the list.
|
||||
:return: a :class:`list` of all the values for the key.
|
||||
"""
|
||||
try:
|
||||
rv = dict.__getitem__(self, key)
|
||||
except KeyError:
|
||||
return []
|
||||
if type is None:
|
||||
return list(rv)
|
||||
result = []
|
||||
for item in rv:
|
||||
try:
|
||||
result.append(type(item))
|
||||
except ValueError:
|
||||
pass
|
||||
return result
|
||||
|
||||
def setlist(self, key, new_list):
|
||||
"""Remove the old values for a key and add new ones. Note that the list
|
||||
you pass the values in will be shallow-copied before it is inserted in
|
||||
the dictionary.
|
||||
|
||||
>>> d = MultiDict()
|
||||
>>> d.setlist('foo', ['1', '2'])
|
||||
>>> d['foo']
|
||||
'1'
|
||||
>>> d.getlist('foo')
|
||||
['1', '2']
|
||||
|
||||
:param key: The key for which the values are set.
|
||||
:param new_list: An iterable with the new values for the key. Old values
|
||||
are removed first.
|
||||
"""
|
||||
dict.__setitem__(self, key, list(new_list))
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
"""Returns the value for the key if it is in the dict, otherwise it
|
||||
returns `default` and sets that value for `key`.
|
||||
|
||||
:param key: The key to be looked up.
|
||||
:param default: The default value to be returned if the key is not
|
||||
in the dict. If not further specified it's `None`.
|
||||
"""
|
||||
if key not in self:
|
||||
self[key] = default
|
||||
else:
|
||||
default = self[key]
|
||||
return default
|
||||
|
||||
def setlistdefault(self, key, default_list=None):
|
||||
"""Like `setdefault` but sets multiple values. The list returned
|
||||
is not a copy, but the list that is actually used internally. This
|
||||
means that you can put new values into the dict by appending items
|
||||
to the list:
|
||||
|
||||
>>> d = MultiDict({"foo": 1})
|
||||
>>> d.setlistdefault("foo").extend([2, 3])
|
||||
>>> d.getlist("foo")
|
||||
[1, 2, 3]
|
||||
|
||||
:param key: The key to be looked up.
|
||||
:param default: An iterable of default values. It is either copied
|
||||
(in case it was a list) or converted into a list
|
||||
before returned.
|
||||
:return: a :class:`list`
|
||||
"""
|
||||
if key not in self:
|
||||
default_list = list(default_list or ())
|
||||
dict.__setitem__(self, key, default_list)
|
||||
else:
|
||||
default_list = dict.__getitem__(self, key)
|
||||
return default_list
|
||||
|
||||
def items(self, multi=False):
|
||||
"""Return a list of ``(key, value)`` pairs.
|
||||
|
||||
:param multi: If set to `True` the list returned will have a
|
||||
pair for each value of each key. Otherwise it
|
||||
will only contain pairs for the first value of
|
||||
each key.
|
||||
|
||||
:return: a :class:`list`
|
||||
"""
|
||||
return list(self.iteritems(multi))
|
||||
|
||||
def lists(self):
|
||||
"""Return a list of ``(key, values)`` pairs, where values is the list of
|
||||
all values associated with the key.
|
||||
|
||||
:return: a :class:`list`
|
||||
"""
|
||||
return list(self.iterlists())
|
||||
|
||||
def values(self):
|
||||
"""Returns a list of the first value on every key's value list.
|
||||
|
||||
:return: a :class:`list`.
|
||||
"""
|
||||
return [self[key] for key in self.iterkeys()]
|
||||
|
||||
def listvalues(self):
|
||||
"""Return a list of all values associated with a key. Zipping
|
||||
:meth:`keys` and this is the same as calling :meth:`lists`:
|
||||
|
||||
>>> d = MultiDict({"foo": [1, 2, 3]})
|
||||
>>> zip(d.keys(), d.listvalues()) == d.lists()
|
||||
True
|
||||
|
||||
:return: a :class:`list`
|
||||
"""
|
||||
return list(self.iterlistvalues())
|
||||
|
||||
def iteritems(self, multi=False):
|
||||
"""Like :meth:`items` but returns an iterator."""
|
||||
for key, values in dict.iteritems(self):
|
||||
if multi:
|
||||
for value in values:
|
||||
yield key, value
|
||||
else:
|
||||
yield key, values[0]
|
||||
|
||||
def iterlists(self):
|
||||
"""Like :meth:`items` but returns an iterator."""
|
||||
for key, values in dict.iteritems(self):
|
||||
yield key, list(values)
|
||||
|
||||
def itervalues(self):
|
||||
"""Like :meth:`values` but returns an iterator."""
|
||||
for values in dict.itervalues(self):
|
||||
yield values[0]
|
||||
|
||||
def iterlistvalues(self):
|
||||
"""Like :meth:`listvalues` but returns an iterator."""
|
||||
return dict.itervalues(self)
|
||||
|
||||
def copy(self):
|
||||
"""Return a shallow copy of this object."""
|
||||
return self.__class__(self)
|
||||
|
||||
def to_dict(self, flat=True):
|
||||
"""Return the contents as regular dict. If `flat` is `True` the
|
||||
returned dict will only have the first item present, if `flat` is
|
||||
`False` all values will be returned as lists.
|
||||
|
||||
:param flat: If set to `False` the dict returned will have lists
|
||||
with all the values in it. Otherwise it will only
|
||||
contain the first value for each key.
|
||||
:return: a :class:`dict`
|
||||
"""
|
||||
if flat:
|
||||
return dict(self.iteritems())
|
||||
return dict(self.lists())
|
||||
|
||||
def update(self, other_dict):
|
||||
"""update() extends rather than replaces existing key lists."""
|
||||
for key, value in iter_multi_items(other_dict):
|
||||
MultiDict.add(self, key, value)
|
||||
|
||||
def pop(self, key, default=_missing):
|
||||
"""Pop the first item for a list on the dict. Afterwards the
|
||||
key is removed from the dict, so additional values are discarded:
|
||||
|
||||
>>> d = MultiDict({"foo": [1, 2, 3]})
|
||||
>>> d.pop("foo")
|
||||
1
|
||||
>>> "foo" in d
|
||||
False
|
||||
|
||||
:param key: the key to pop.
|
||||
:param default: if provided the value to return if the key was
|
||||
not in the dictionary.
|
||||
"""
|
||||
try:
|
||||
return dict.pop(self, key)[0]
|
||||
except KeyError, e:
|
||||
if default is not _missing:
|
||||
return default
|
||||
raise KeyError(str(e))
|
||||
|
||||
def popitem(self):
|
||||
"""Pop an item from the dict."""
|
||||
try:
|
||||
item = dict.popitem(self)
|
||||
return (item[0], item[1][0])
|
||||
except KeyError, e:
|
||||
raise KeyError(str(e))
|
||||
|
||||
def poplist(self, key):
|
||||
"""Pop the list for a key from the dict. If the key is not in the dict
|
||||
an empty list is returned.
|
||||
|
||||
.. versionchanged:: 0.5
|
||||
If the key does no longer exist a list is returned instead of
|
||||
raising an error.
|
||||
"""
|
||||
return dict.pop(self, key, [])
|
||||
|
||||
def popitemlist(self):
|
||||
"""Pop a ``(key, list)`` tuple from the dict."""
|
||||
try:
|
||||
return dict.popitem(self)
|
||||
except KeyError, e:
|
||||
raise KeyError(str(e))
|
||||
|
||||
def __copy__(self):
|
||||
return self.copy()
|
||||
|
||||
def __repr__(self):
|
||||
return '%s(%r)' % (self.__class__.__name__, self.items(multi=True))
|
||||
48
requests/packages/urllib3/__init__.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# urllib3/__init__.py
|
||||
# Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
|
||||
#
|
||||
# This module is part of urllib3 and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""
|
||||
urllib3 - Thread-safe connection pooling and re-using.
|
||||
"""
|
||||
|
||||
__author__ = 'Andrey Petrov (andrey.petrov@shazow.net)'
|
||||
__license__ = 'MIT'
|
||||
__version__ = '1.0.2'
|
||||
|
||||
|
||||
from .connectionpool import (
|
||||
HTTPConnectionPool,
|
||||
HTTPSConnectionPool,
|
||||
connection_from_url,
|
||||
get_host,
|
||||
make_headers)
|
||||
|
||||
|
||||
from .exceptions import (
|
||||
HTTPError,
|
||||
MaxRetryError,
|
||||
SSLError,
|
||||
TimeoutError)
|
||||
|
||||
from .poolmanager import PoolManager, ProxyManager, proxy_from_url
|
||||
from .response import HTTPResponse
|
||||
from .filepost import encode_multipart_formdata
|
||||
|
||||
|
||||
# Set default logging handler to avoid "No handler found" warnings.
|
||||
import logging
|
||||
try:
|
||||
from logging import NullHandler
|
||||
except ImportError:
|
||||
class NullHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
pass
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
|
||||
# ... Clean up.
|
||||
del logging
|
||||
del NullHandler
|
||||
131
requests/packages/urllib3/_collections.py
Normal file
@@ -0,0 +1,131 @@
|
||||
# urllib3/_collections.py
|
||||
# Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
|
||||
#
|
||||
# This module is part of urllib3 and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
from collections import deque
|
||||
|
||||
from threading import RLock
|
||||
|
||||
__all__ = ['RecentlyUsedContainer']
|
||||
|
||||
|
||||
class AccessEntry(object):
|
||||
__slots__ = ('key', 'is_valid')
|
||||
|
||||
def __init__(self, key, is_valid=True):
|
||||
self.key = key
|
||||
self.is_valid = is_valid
|
||||
|
||||
|
||||
class RecentlyUsedContainer(dict):
|
||||
"""
|
||||
Provides a dict-like that maintains up to ``maxsize`` keys while throwing
|
||||
away the least-recently-used keys beyond ``maxsize``.
|
||||
"""
|
||||
|
||||
# If len(self.access_log) exceeds self._maxsize * CLEANUP_FACTOR, then we
|
||||
# will attempt to cleanup the invalidated entries in the access_log
|
||||
# datastructure during the next 'get' operation.
|
||||
CLEANUP_FACTOR = 10
|
||||
|
||||
def __init__(self, maxsize=10):
|
||||
self._maxsize = maxsize
|
||||
|
||||
self._container = {}
|
||||
|
||||
# We use a deque to to store our keys ordered by the last access.
|
||||
self.access_log = deque()
|
||||
self.access_log_lock = RLock()
|
||||
|
||||
# We look up the access log entry by the key to invalidate it so we can
|
||||
# insert a new authorative entry at the head without having to dig and
|
||||
# find the old entry for removal immediately.
|
||||
self.access_lookup = {}
|
||||
|
||||
# Trigger a heap cleanup when we get past this size
|
||||
self.access_log_limit = maxsize * self.CLEANUP_FACTOR
|
||||
|
||||
def _invalidate_entry(self, key):
|
||||
"If exists: Invalidate old entry and return it."
|
||||
old_entry = self.access_lookup.get(key)
|
||||
if old_entry:
|
||||
old_entry.is_valid = False
|
||||
|
||||
return old_entry
|
||||
|
||||
def _push_entry(self, key):
|
||||
"Push entry onto our access log, invalidate the old entry if exists."
|
||||
self._invalidate_entry(key)
|
||||
|
||||
new_entry = AccessEntry(key)
|
||||
self.access_lookup[key] = new_entry
|
||||
|
||||
self.access_log_lock.acquire()
|
||||
self.access_log.appendleft(new_entry)
|
||||
self.access_log_lock.release()
|
||||
|
||||
def _prune_entries(self, num):
|
||||
"Pop entries from our access log until we popped ``num`` valid ones."
|
||||
while num > 0:
|
||||
self.access_log_lock.acquire()
|
||||
p = self.access_log.pop()
|
||||
self.access_log_lock.release()
|
||||
|
||||
if not p.is_valid:
|
||||
continue # Invalidated entry, skip
|
||||
|
||||
dict.pop(self, p.key, None)
|
||||
self.access_lookup.pop(p.key, None)
|
||||
num -= 1
|
||||
|
||||
def _prune_invalidated_entries(self):
|
||||
"Rebuild our access_log without the invalidated entries."
|
||||
self.access_log_lock.acquire()
|
||||
self.access_log = deque(e for e in self.access_log if e.is_valid)
|
||||
self.access_log_lock.release()
|
||||
|
||||
def _get_ordered_access_keys(self):
|
||||
"Return ordered access keys for inspection. Used for testing."
|
||||
self.access_log_lock.acquire()
|
||||
r = [e.key for e in self.access_log if e.is_valid]
|
||||
self.access_log_lock.release()
|
||||
|
||||
return r
|
||||
|
||||
def __getitem__(self, key):
|
||||
item = dict.get(self, key)
|
||||
|
||||
if not item:
|
||||
raise KeyError(key)
|
||||
|
||||
# Insert new entry with new high priority, also implicitly invalidates
|
||||
# the old entry.
|
||||
self._push_entry(key)
|
||||
|
||||
if len(self.access_log) > self.access_log_limit:
|
||||
# Heap is getting too big, try to clean up any tailing invalidated
|
||||
# entries.
|
||||
self._prune_invalidated_entries()
|
||||
|
||||
return item
|
||||
|
||||
def __setitem__(self, key, item):
|
||||
# Add item to our container and access log
|
||||
dict.__setitem__(self, key, item)
|
||||
self._push_entry(key)
|
||||
|
||||
# Discard invalid and excess entries
|
||||
self._prune_entries(len(self) - self._maxsize)
|
||||
|
||||
def __delitem__(self, key):
|
||||
self._invalidate_entry(key)
|
||||
self.access_lookup.pop(key, None)
|
||||
dict.__delitem__(self, key)
|
||||
|
||||
def get(self, key, default=None):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
return default
|
||||
548
requests/packages/urllib3/connectionpool.py
Normal file
@@ -0,0 +1,548 @@
|
||||
# urllib3/connectionpool.py
|
||||
# Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
|
||||
#
|
||||
# This module is part of urllib3 and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
import logging
|
||||
import socket
|
||||
|
||||
|
||||
from httplib import HTTPConnection, HTTPSConnection, HTTPException
|
||||
from Queue import Queue, Empty, Full
|
||||
from select import select
|
||||
from socket import error as SocketError, timeout as SocketTimeout
|
||||
|
||||
from .packages.ssl_match_hostname import match_hostname, CertificateError
|
||||
|
||||
try:
|
||||
import ssl
|
||||
BaseSSLError = ssl.SSLError
|
||||
except ImportError:
|
||||
ssl = None
|
||||
BaseSSLError = None
|
||||
|
||||
|
||||
from .request import RequestMethods
|
||||
from .response import HTTPResponse
|
||||
from .exceptions import (
|
||||
SSLError,
|
||||
MaxRetryError,
|
||||
TimeoutError,
|
||||
HostChangedError,
|
||||
EmptyPoolError,
|
||||
)
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
_Default = object()
|
||||
|
||||
|
||||
## Connection objects (extension of httplib)
|
||||
|
||||
class VerifiedHTTPSConnection(HTTPSConnection):
|
||||
"""
|
||||
Based on httplib.HTTPSConnection but wraps the socket with
|
||||
SSL certification.
|
||||
"""
|
||||
cert_reqs = None
|
||||
ca_certs = None
|
||||
|
||||
def set_cert(self, key_file=None, cert_file=None,
|
||||
cert_reqs='CERT_NONE', ca_certs=None):
|
||||
ssl_req_scheme = {
|
||||
'CERT_NONE': ssl.CERT_NONE,
|
||||
'CERT_OPTIONAL': ssl.CERT_OPTIONAL,
|
||||
'CERT_REQUIRED': ssl.CERT_REQUIRED
|
||||
}
|
||||
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
self.cert_reqs = ssl_req_scheme.get(cert_reqs) or ssl.CERT_NONE
|
||||
self.ca_certs = ca_certs
|
||||
|
||||
def connect(self):
|
||||
# Add certificate verification
|
||||
sock = socket.create_connection((self.host, self.port), self.timeout)
|
||||
|
||||
# Wrap socket using verification with the root certs in
|
||||
# trusted_root_certs
|
||||
self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
|
||||
cert_reqs=self.cert_reqs,
|
||||
ca_certs=self.ca_certs)
|
||||
if self.ca_certs:
|
||||
match_hostname(self.sock.getpeercert(), self.host)
|
||||
|
||||
## Pool objects
|
||||
|
||||
class ConnectionPool(object):
|
||||
"""
|
||||
Base class for all connection pools, such as
|
||||
:class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class HTTPConnectionPool(ConnectionPool, RequestMethods):
|
||||
"""
|
||||
Thread-safe connection pool for one host.
|
||||
|
||||
:param host:
|
||||
Host used for this HTTP Connection (e.g. "localhost"), passed into
|
||||
:class:`httplib.HTTPConnection`.
|
||||
|
||||
:param port:
|
||||
Port used for this HTTP Connection (None is equivalent to 80), passed
|
||||
into :class:`httplib.HTTPConnection`.
|
||||
|
||||
:param strict:
|
||||
Causes BadStatusLine to be raised if the status line can't be parsed
|
||||
as a valid HTTP/1.0 or 1.1 status line, passed into
|
||||
:class:`httplib.HTTPConnection`.
|
||||
|
||||
:param timeout:
|
||||
Socket timeout for each individual connection, can be a float. None
|
||||
disables timeout.
|
||||
|
||||
:param maxsize:
|
||||
Number of connections to save that can be reused. More than 1 is useful
|
||||
in multithreaded situations. If ``block`` is set to false, more
|
||||
connections will be created but they will not be saved once they've
|
||||
been used.
|
||||
|
||||
:param block:
|
||||
If set to True, no more than ``maxsize`` connections will be used at
|
||||
a time. When no free connections are available, the call will block
|
||||
until a connection has been released. This is a useful side effect for
|
||||
particular multithreaded situations where one does not want to use more
|
||||
than maxsize connections per host to prevent flooding.
|
||||
|
||||
:param headers:
|
||||
Headers to include with all requests, unless other headers are given
|
||||
explicitly.
|
||||
"""
|
||||
|
||||
scheme = 'http'
|
||||
|
||||
def __init__(self, host, port=None, strict=False, timeout=None, maxsize=1,
|
||||
block=False, headers=None):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.strict = strict
|
||||
self.timeout = timeout
|
||||
self.pool = Queue(maxsize)
|
||||
self.block = block
|
||||
self.headers = headers or {}
|
||||
|
||||
# Fill the queue up so that doing get() on it will block properly
|
||||
for _ in xrange(maxsize):
|
||||
self.pool.put(None)
|
||||
|
||||
# These are mostly for testing and debugging purposes.
|
||||
self.num_connections = 0
|
||||
self.num_requests = 0
|
||||
|
||||
def _new_conn(self):
|
||||
"""
|
||||
Return a fresh :class:`httplib.HTTPConnection`.
|
||||
"""
|
||||
self.num_connections += 1
|
||||
log.info("Starting new HTTP connection (%d): %s" %
|
||||
(self.num_connections, self.host))
|
||||
return HTTPConnection(host=self.host, port=self.port)
|
||||
|
||||
def _get_conn(self, timeout=None):
|
||||
"""
|
||||
Get a connection. Will return a pooled connection if one is available.
|
||||
|
||||
If no connections are available and :prop:`.block` is ``False``, then a
|
||||
fresh connection is returned.
|
||||
|
||||
:param timeout:
|
||||
Seconds to wait before giving up and raising
|
||||
:class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and
|
||||
:prop:`.block` is ``True``.
|
||||
"""
|
||||
conn = None
|
||||
try:
|
||||
conn = self.pool.get(block=self.block, timeout=timeout)
|
||||
|
||||
# If this is a persistent connection, check if it got disconnected
|
||||
if conn and conn.sock and select([conn.sock], [], [], 0.0)[0]:
|
||||
# Either data is buffered (bad), or the connection is dropped.
|
||||
log.info("Resetting dropped connection: %s" % self.host)
|
||||
conn.close()
|
||||
|
||||
except Empty:
|
||||
if self.block:
|
||||
raise EmptyPoolError("Pool reached maximum size and no more "
|
||||
"connections are allowed.")
|
||||
pass # Oh well, we'll create a new connection then
|
||||
|
||||
return conn or self._new_conn()
|
||||
|
||||
def _put_conn(self, conn):
|
||||
"""
|
||||
Put a connection back into the pool.
|
||||
|
||||
:param conn:
|
||||
Connection object for the current host and port as returned by
|
||||
:meth:`._new_conn` or :meth:`._get_conn`.
|
||||
|
||||
If the pool is already full, the connection is discarded because we
|
||||
exceeded maxsize. If connections are discarded frequently, then maxsize
|
||||
should be increased.
|
||||
"""
|
||||
try:
|
||||
self.pool.put(conn, block=False)
|
||||
except Full:
|
||||
# This should never happen if self.block == True
|
||||
log.warning("HttpConnectionPool is full, discarding connection: %s"
|
||||
% self.host)
|
||||
|
||||
def _make_request(self, conn, method, url, timeout=_Default,
|
||||
**httplib_request_kw):
|
||||
"""
|
||||
Perform a request on a given httplib connection object taken from our
|
||||
pool.
|
||||
"""
|
||||
self.num_requests += 1
|
||||
|
||||
if timeout is _Default:
|
||||
timeout = self.timeout
|
||||
|
||||
conn.timeout = timeout # This only does anything in Py26+
|
||||
|
||||
conn.request(method, url, **httplib_request_kw)
|
||||
conn.sock.settimeout(timeout)
|
||||
httplib_response = conn.getresponse()
|
||||
|
||||
log.debug("\"%s %s %s\" %s %s" %
|
||||
(method, url,
|
||||
conn._http_vsn_str, # pylint: disable-msg=W0212
|
||||
httplib_response.status, httplib_response.length))
|
||||
|
||||
return httplib_response
|
||||
|
||||
|
||||
def is_same_host(self, url):
|
||||
"""
|
||||
Check if the given ``url`` is a member of the same host as this
|
||||
conncetion pool.
|
||||
"""
|
||||
# TODO: Add optional support for socket.gethostbyname checking.
|
||||
return (url.startswith('/') or
|
||||
get_host(url) == (self.scheme, self.host, self.port))
|
||||
|
||||
def urlopen(self, method, url, body=None, headers=None, retries=3,
|
||||
redirect=True, assert_same_host=True, timeout=_Default,
|
||||
pool_timeout=None, release_conn=None, **response_kw):
|
||||
"""
|
||||
Get a connection from the pool and perform an HTTP request. This is the
|
||||
lowest level call for making a request, so you'll need to specify all
|
||||
the raw details.
|
||||
|
||||
.. note::
|
||||
|
||||
More commonly, it's appropriate to use a convenience method provided
|
||||
by :class:`.RequestMethods`, such as :meth:`.request`.
|
||||
|
||||
.. note::
|
||||
|
||||
`release_conn` will only behave as expected if
|
||||
`preload_content=False` because we want to make
|
||||
`preload_content=False` the default behaviour someday soon without
|
||||
breaking backwards compatibility.
|
||||
|
||||
:param method:
|
||||
HTTP request method (such as GET, POST, PUT, etc.)
|
||||
|
||||
:param body:
|
||||
Data to send in the request body (useful for creating
|
||||
POST requests, see HTTPConnectionPool.post_url for
|
||||
more convenience).
|
||||
|
||||
:param headers:
|
||||
Dictionary of custom headers to send, such as User-Agent,
|
||||
If-None-Match, etc. If None, pool headers are used. If provided,
|
||||
these headers completely replace any pool-specific headers.
|
||||
|
||||
:param retries:
|
||||
Number of retries to allow before raising a MaxRetryError exception.
|
||||
|
||||
:param redirect:
|
||||
Automatically handle redirects (status codes 301, 302, 303, 307),
|
||||
each redirect counts as a retry.
|
||||
|
||||
:param assert_same_host:
|
||||
If ``True``, will make sure that the host of the pool requests is
|
||||
consistent else will raise HostChangedError. When False, you can
|
||||
use the pool on an HTTP proxy and request foreign hosts.
|
||||
|
||||
:param timeout:
|
||||
If specified, overrides the default timeout for this one request.
|
||||
|
||||
:param pool_timeout:
|
||||
If set and the pool is set to block=True, then this method will
|
||||
block for ``pool_timeout`` seconds and raise EmptyPoolError if no
|
||||
connection is available within the time period.
|
||||
|
||||
:param release_conn:
|
||||
If False, then the urlopen call will not release the connection
|
||||
back into the pool once a response is received (but will release if
|
||||
you read the entire contents of the response such as when
|
||||
`preload_content=True`). This is useful if you're not preloading
|
||||
the response's content immediately. You will need to call
|
||||
``r.release_conn()`` on the response ``r`` to return the connection
|
||||
back into the pool. If None, it takes the value of
|
||||
``response_kw.get('preload_content', True)``.
|
||||
|
||||
:param \**response_kw:
|
||||
Additional parameters are passed to
|
||||
:meth:`urllib3.response.HTTPResponse.from_httplib`
|
||||
"""
|
||||
if headers is None:
|
||||
headers = self.headers
|
||||
|
||||
if retries < 0:
|
||||
raise MaxRetryError(url)
|
||||
|
||||
if timeout is _Default:
|
||||
timeout = self.timeout
|
||||
|
||||
if release_conn is None:
|
||||
release_conn = response_kw.get('preload_content', True)
|
||||
|
||||
# Check host
|
||||
if assert_same_host and not self.is_same_host(url):
|
||||
host = "%s://%s" % (self.scheme, self.host)
|
||||
if self.port:
|
||||
host = "%s:%d" % (host, self.port)
|
||||
|
||||
raise HostChangedError(host, url, retries - 1)
|
||||
|
||||
conn = None
|
||||
|
||||
try:
|
||||
# Request a connection from the queue
|
||||
# (Could raise SocketError: Bad file descriptor)
|
||||
conn = self._get_conn(timeout=pool_timeout)
|
||||
|
||||
# Make the request on the httplib connection object
|
||||
httplib_response = self._make_request(conn, method, url,
|
||||
timeout=timeout,
|
||||
body=body, headers=headers)
|
||||
|
||||
# If we're going to release the connection in ``finally:``, then
|
||||
# the request doesn't need to know about the connection. Otherwise
|
||||
# it will also try to release it and we'll have a double-release
|
||||
# mess.
|
||||
response_conn = not release_conn and conn
|
||||
|
||||
# Import httplib's response into our own wrapper object
|
||||
response = HTTPResponse.from_httplib(httplib_response,
|
||||
pool=self,
|
||||
connection=response_conn,
|
||||
**response_kw)
|
||||
|
||||
# else:
|
||||
# The connection will be put back into the pool when
|
||||
# ``response.release_conn()`` is called (implicitly by
|
||||
# ``response.read()``)
|
||||
|
||||
except (Empty), e:
|
||||
# Timed out by queue
|
||||
raise TimeoutError("Request timed out. (pool_timeout=%s)" %
|
||||
pool_timeout)
|
||||
|
||||
except (SocketTimeout), e:
|
||||
# Timed out by socket
|
||||
raise TimeoutError("Request timed out. (timeout=%s)" %
|
||||
timeout)
|
||||
|
||||
except (BaseSSLError), e:
|
||||
# SSL certificate error
|
||||
raise SSLError(e)
|
||||
|
||||
except (CertificateError), e:
|
||||
# Name mismatch
|
||||
raise SSLError(e)
|
||||
|
||||
except (HTTPException, SocketError), e:
|
||||
# Connection broken, discard. It will be replaced next _get_conn().
|
||||
conn = None
|
||||
|
||||
finally:
|
||||
if conn and release_conn:
|
||||
# Put the connection back to be reused
|
||||
self._put_conn(conn)
|
||||
|
||||
if not conn:
|
||||
log.warn("Retrying (%d attempts remain) after connection "
|
||||
"broken by '%r': %s" % (retries, e, url))
|
||||
return self.urlopen(method, url, body, headers, retries - 1,
|
||||
redirect, assert_same_host) # Try again
|
||||
|
||||
# Handle redirect?
|
||||
redirect_location = redirect and response.get_redirect_location()
|
||||
if redirect_location:
|
||||
log.info("Redirecting %s -> %s" % (url, redirect_location))
|
||||
return self.urlopen(method, redirect_location, body, headers,
|
||||
retries - 1, redirect, assert_same_host)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
class HTTPSConnectionPool(HTTPConnectionPool):
|
||||
"""
|
||||
Same as :class:`.HTTPConnectionPool`, but HTTPS.
|
||||
|
||||
When Python is compiled with the :mod:`ssl` module, then
|
||||
:class:`.VerifiedHTTPSConnection` is used, which *can* verify certificates,
|
||||
instead of :class:httplib.HTTPSConnection`.
|
||||
|
||||
The ``key_file``, ``cert_file``, ``cert_reqs``, and ``ca_certs`` parameters
|
||||
are only used if :mod:`ssl` is available and are fed into
|
||||
:meth:`ssl.wrap_socket` to upgrade the connection socket into an SSL socket.
|
||||
"""
|
||||
|
||||
scheme = 'https'
|
||||
|
||||
def __init__(self, host, port=None,
|
||||
strict=False, timeout=None, maxsize=1,
|
||||
block=False, headers=None,
|
||||
key_file=None, cert_file=None,
|
||||
cert_reqs='CERT_NONE', ca_certs=None):
|
||||
|
||||
super(HTTPSConnectionPool, self).__init__(host, port,
|
||||
strict, timeout, maxsize,
|
||||
block, headers)
|
||||
self.key_file = key_file
|
||||
self.cert_file = cert_file
|
||||
self.cert_reqs = cert_reqs
|
||||
self.ca_certs = ca_certs
|
||||
|
||||
def _new_conn(self):
|
||||
"""
|
||||
Return a fresh :class:`httplib.HTTPSConnection`.
|
||||
"""
|
||||
self.num_connections += 1
|
||||
log.info("Starting new HTTPS connection (%d): %s"
|
||||
% (self.num_connections, self.host))
|
||||
|
||||
if not ssl:
|
||||
return HTTPSConnection(host=self.host, port=self.port)
|
||||
|
||||
connection = VerifiedHTTPSConnection(host=self.host, port=self.port)
|
||||
connection.set_cert(key_file=self.key_file, cert_file=self.cert_file,
|
||||
cert_reqs=self.cert_reqs, ca_certs=self.ca_certs)
|
||||
return connection
|
||||
|
||||
|
||||
## Helpers
|
||||
|
||||
def make_headers(keep_alive=None, accept_encoding=None, user_agent=None,
|
||||
basic_auth=None):
|
||||
"""
|
||||
Shortcuts for generating request headers.
|
||||
|
||||
:param keep_alive:
|
||||
If ``True``, adds 'connection: keep-alive' header.
|
||||
|
||||
:param accept_encoding:
|
||||
Can be a boolean, list, or string.
|
||||
``True`` translates to 'gzip,deflate'.
|
||||
List will get joined by comma.
|
||||
String will be used as provided.
|
||||
|
||||
:param user_agent:
|
||||
String representing the user-agent you want, such as
|
||||
"python-urllib3/0.6"
|
||||
|
||||
:param basic_auth:
|
||||
Colon-separated username:password string for 'authorization: basic ...'
|
||||
auth header.
|
||||
|
||||
Example: ::
|
||||
|
||||
>>> make_headers(keep_alive=True, user_agent="Batman/1.0")
|
||||
{'connection': 'keep-alive', 'user-agent': 'Batman/1.0'}
|
||||
>>> make_headers(accept_encoding=True)
|
||||
{'accept-encoding': 'gzip,deflate'}
|
||||
"""
|
||||
headers = {}
|
||||
if accept_encoding:
|
||||
if isinstance(accept_encoding, str):
|
||||
pass
|
||||
elif isinstance(accept_encoding, list):
|
||||
accept_encoding = ','.join(accept_encoding)
|
||||
else:
|
||||
accept_encoding = 'gzip,deflate'
|
||||
headers['accept-encoding'] = accept_encoding
|
||||
|
||||
if user_agent:
|
||||
headers['user-agent'] = user_agent
|
||||
|
||||
if keep_alive:
|
||||
headers['connection'] = 'keep-alive'
|
||||
|
||||
if basic_auth:
|
||||
headers['authorization'] = 'Basic ' + \
|
||||
basic_auth.encode('base64').strip()
|
||||
|
||||
return headers
|
||||
|
||||
|
||||
def get_host(url):
|
||||
"""
|
||||
Given a url, return its scheme, host and port (None if it's not there).
|
||||
|
||||
For example: ::
|
||||
|
||||
>>> get_host('http://google.com/mail/')
|
||||
('http', 'google.com', None)
|
||||
>>> get_host('google.com:80')
|
||||
('http', 'google.com', 80)
|
||||
"""
|
||||
# This code is actually similar to urlparse.urlsplit, but much
|
||||
# simplified for our needs.
|
||||
port = None
|
||||
scheme = 'http'
|
||||
if '://' in url:
|
||||
scheme, url = url.split('://', 1)
|
||||
if '/' in url:
|
||||
url, _path = url.split('/', 1)
|
||||
if '@' in url:
|
||||
_auth, url = url.split('@', 1)
|
||||
if ':' in url:
|
||||
url, port = url.split(':', 1)
|
||||
port = int(port)
|
||||
return scheme, url, port
|
||||
|
||||
|
||||
def connection_from_url(url, **kw):
|
||||
"""
|
||||
Given a url, return an :class:`.ConnectionPool` instance of its host.
|
||||
|
||||
This is a shortcut for not having to parse out the scheme, host, and port
|
||||
of the url before creating an :class:`.ConnectionPool` instance.
|
||||
|
||||
:param url:
|
||||
Absolute URL string that must include the scheme. Port is optional.
|
||||
|
||||
:param \**kw:
|
||||
Passes additional parameters to the constructor of the appropriate
|
||||
:class:`.ConnectionPool`. Useful for specifying things like
|
||||
timeout, maxsize, headers, etc.
|
||||
|
||||
Example: ::
|
||||
|
||||
>>> conn = connection_from_url('http://google.com/')
|
||||
>>> r = conn.request('GET', '/')
|
||||
"""
|
||||
scheme, host, port = get_host(url)
|
||||
if scheme == 'https':
|
||||
return HTTPSConnectionPool(host, port=port, **kw)
|
||||
else:
|
||||
return HTTPConnectionPool(host, port=port, **kw)
|
||||
117
requests/packages/urllib3/contrib/ntlmpool.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# urllib3/contrib/ntlmpool.py
|
||||
# Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
|
||||
#
|
||||
# This module is part of urllib3 and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""
|
||||
NTLM authenticating pool, contributed by erikcederstran
|
||||
|
||||
Issue #10, see: http://code.google.com/p/urllib3/issues/detail?id=10
|
||||
"""
|
||||
|
||||
import httplib
|
||||
from logging import getLogger
|
||||
from ntlm import ntlm
|
||||
|
||||
from urllib3 import HTTPSConnectionPool
|
||||
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
class NTLMConnectionPool(HTTPSConnectionPool):
|
||||
"""
|
||||
Implements an NTLM authentication version of an urllib3 connection pool
|
||||
"""
|
||||
|
||||
scheme = 'https'
|
||||
|
||||
def __init__(self, user, pw, authurl, *args, **kwargs):
|
||||
"""
|
||||
authurl is a random URL on the server that is protected by NTLM.
|
||||
user is the Windows user, probably in the DOMAIN\username format.
|
||||
pw is the password for the user.
|
||||
"""
|
||||
super(NTLMConnectionPool, self).__init__(*args, **kwargs)
|
||||
self.authurl = authurl
|
||||
self.rawuser = user
|
||||
user_parts = user.split('\\', 1)
|
||||
self.domain = user_parts[0].upper()
|
||||
self.user = user_parts[1]
|
||||
self.pw = pw
|
||||
|
||||
def _new_conn(self):
|
||||
# Performs the NTLM handshake that secures the connection. The socket
|
||||
# must be kept open while requests are performed.
|
||||
self.num_connections += 1
|
||||
log.debug('Starting NTLM HTTPS connection no. %d: https://%s%s' %
|
||||
(self.num_connections, self.host, self.authurl))
|
||||
|
||||
headers = {}
|
||||
headers['Connection'] = 'Keep-Alive'
|
||||
req_header = 'Authorization'
|
||||
resp_header = 'www-authenticate'
|
||||
|
||||
conn = httplib.HTTPSConnection(host=self.host, port=self.port)
|
||||
|
||||
# Send negotiation message
|
||||
headers[req_header] = (
|
||||
'NTLM %s' % ntlm.create_NTLM_NEGOTIATE_MESSAGE(self.rawuser))
|
||||
log.debug('Request headers: %s' % headers)
|
||||
conn.request('GET', self.authurl, None, headers)
|
||||
res = conn.getresponse()
|
||||
reshdr = dict(res.getheaders())
|
||||
log.debug('Response status: %s %s' % (res.status, res.reason))
|
||||
log.debug('Response headers: %s' % reshdr)
|
||||
log.debug('Response data: %s [...]' % res.read(100))
|
||||
|
||||
# Remove the reference to the socket, so that it can not be closed by
|
||||
# the response object (we want to keep the socket open)
|
||||
res.fp = None
|
||||
|
||||
# Server should respond with a challenge message
|
||||
auth_header_values = reshdr[resp_header].split(', ')
|
||||
auth_header_value = None
|
||||
for s in auth_header_values:
|
||||
if s[:5] == 'NTLM ':
|
||||
auth_header_value = s[5:]
|
||||
if auth_header_value is None:
|
||||
raise Exception('Unexpected %s response header: %s' %
|
||||
(resp_header, reshdr[resp_header]))
|
||||
|
||||
# Send authentication message
|
||||
ServerChallenge, NegotiateFlags = \
|
||||
ntlm.parse_NTLM_CHALLENGE_MESSAGE(auth_header_value)
|
||||
auth_msg = ntlm.create_NTLM_AUTHENTICATE_MESSAGE(ServerChallenge,
|
||||
self.user,
|
||||
self.domain,
|
||||
self.pw,
|
||||
NegotiateFlags)
|
||||
headers[req_header] = 'NTLM %s' % auth_msg
|
||||
log.debug('Request headers: %s' % headers)
|
||||
conn.request('GET', self.authurl, None, headers)
|
||||
res = conn.getresponse()
|
||||
log.debug('Response status: %s %s' % (res.status, res.reason))
|
||||
log.debug('Response headers: %s' % dict(res.getheaders()))
|
||||
log.debug('Response data: %s [...]' % res.read()[:100])
|
||||
if res.status != 200:
|
||||
if res.status == 401:
|
||||
raise Exception('Server rejected request: wrong '
|
||||
'username or password')
|
||||
raise Exception('Wrong server response: %s %s' %
|
||||
(res.status, res.reason))
|
||||
|
||||
res.fp = None
|
||||
log.debug('Connection established')
|
||||
return conn
|
||||
|
||||
def urlopen(self, method, url, body=None, headers=None, retries=3,
|
||||
redirect=True, assert_same_host=True):
|
||||
if headers is None:
|
||||
headers = {}
|
||||
headers['Connection'] = 'Keep-Alive'
|
||||
return super(NTLMConnectionPool, self).urlopen(method, url, body,
|
||||
headers, retries,
|
||||
redirect,
|
||||
assert_same_host)
|
||||
45
requests/packages/urllib3/exceptions.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# urllib3/exceptions.py
|
||||
# Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
|
||||
#
|
||||
# This module is part of urllib3 and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
## Exceptions
|
||||
|
||||
class HTTPError(Exception):
|
||||
"Base exception used by this module."
|
||||
pass
|
||||
|
||||
|
||||
class SSLError(Exception):
|
||||
"Raised when SSL certificate fails in an HTTPS connection."
|
||||
pass
|
||||
|
||||
|
||||
class MaxRetryError(HTTPError):
|
||||
"Raised when the maximum number of retries is exceeded."
|
||||
def __init__(self, url):
|
||||
HTTPError.__init__(self, "Max retries exceeded for url: %s" % url)
|
||||
self.url = url
|
||||
|
||||
|
||||
class TimeoutError(HTTPError):
|
||||
"Raised when a socket timeout occurs."
|
||||
pass
|
||||
|
||||
|
||||
class HostChangedError(HTTPError):
|
||||
"Raised when an existing pool gets a request for a foreign host."
|
||||
def __init__(self, original_host, new_url, retries=3):
|
||||
HTTPError.__init__(self,
|
||||
"Connection pool with host '%s' tried to open a foreign host: %s" %
|
||||
(original_host, new_url))
|
||||
|
||||
self.original_host = original_host
|
||||
self.new_url = new_url
|
||||
self.retries = retries
|
||||
|
||||
|
||||
class EmptyPoolError(HTTPError):
|
||||
"Raised when a pool runs out of connections and no more are allowed."
|
||||
pass
|
||||
71
requests/packages/urllib3/filepost.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# urllib3/filepost.py
|
||||
# Copyright 2008-2011 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
|
||||
#
|
||||
# This module is part of urllib3 and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
import codecs
|
||||
import mimetools
|
||||
import mimetypes
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO # pylint: disable-msg=W0404
|
||||
|
||||
|
||||
writer = codecs.lookup('utf-8')[3]
|
||||
|
||||
|
||||
def get_content_type(filename):
|
||||
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|
||||
|
||||
|
||||
def encode_multipart_formdata(fields, boundary=None):
|
||||
"""
|
||||
Encode a dictionary of ``fields`` using the multipart/form-data mime format.
|
||||
|
||||
:param fields:
|
||||
Dictionary of fields. The key is treated as the field name, and the
|
||||
value as the body of the form-data. If the value is a tuple of two
|
||||
elements, then the first element is treated as the filename of the
|
||||
form-data section.
|
||||
|
||||
:param boundary:
|
||||
If not specified, then a random boundary will be generated using
|
||||
:func:`mimetools.choose_boundary`.
|
||||
"""
|
||||
body = StringIO()
|
||||
if boundary is None:
|
||||
boundary = mimetools.choose_boundary()
|
||||
|
||||
for fieldname, value in fields.iteritems():
|
||||
body.write('--%s\r\n' % (boundary))
|
||||
|
||||
if isinstance(value, tuple):
|
||||
filename, data = value
|
||||
writer(body).write('Content-Disposition: form-data; name="%s"; '
|
||||
'filename="%s"\r\n' % (fieldname, filename))
|
||||
body.write('Content-Type: %s\r\n\r\n' %
|
||||
(get_content_type(filename)))
|
||||
else:
|
||||
data = value
|
||||
writer(body).write('Content-Disposition: form-data; name="%s"\r\n'
|
||||
% (fieldname))
|
||||
body.write('Content-Type: text/plain\r\n\r\n')
|
||||
|
||||
if isinstance(data, int):
|
||||
data = str(data) # Backwards compatibility
|
||||
|
||||
if isinstance(data, unicode):
|
||||
writer(body).write(data)
|
||||
else:
|
||||
body.write(data)
|
||||
|
||||
body.write('\r\n')
|
||||
|
||||
body.write('--%s--\r\n' % (boundary))
|
||||
|
||||
content_type = 'multipart/form-data; boundary=%s' % boundary
|
||||
|
||||
return body.getvalue(), content_type
|
||||
4
requests/packages/urllib3/packages/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from . import ssl_match_hostname
|
||||
|
||||