Porównaj commity

..

No commits in common. "master" and "v1.8.2" have entirely different histories.

963 zmienionych plików z 15149 dodań i 45010 usunięć

Wyświetl plik

@ -1,29 +0,0 @@
name: Build all packages
on: [push, pull_request]
env:
PACKAGE_INDEX_PATH: /tmp/micropython-lib-deploy
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- name: Setup environment
run: source tools/ci.sh && ci_build_packages_setup
- name: Check manifest files
run: source tools/ci.sh && ci_build_packages_check_manifest
- name: Compile package index
run: source tools/ci.sh && ci_build_packages_compile_index
- name: Compile package examples
run: source tools/ci.sh && ci_build_packages_examples
- name: Publish packages for branch
if: vars.MICROPY_PUBLISH_MIP_INDEX && github.event_name == 'push' && ! github.event.deleted
run: source tools/ci.sh && ci_push_package_index
- name: Upload packages as artifact
uses: actions/upload-artifact@v4
with:
name: packages-${{ github.sha }}
path: ${{ env.PACKAGE_INDEX_PATH }}

Wyświetl plik

@ -1,12 +0,0 @@
name: Cleanup published packages
on: delete
jobs:
cleanup:
runs-on: ubuntu-latest
if: vars.MICROPY_PUBLISH_MIP_INDEX
steps:
- uses: actions/checkout@v3
- name: Clean up published files
run: source tools/ci.sh && ci_cleanup_package_index ${{ github.event.ref }}

Wyświetl plik

@ -1,18 +0,0 @@
name: Check commit message formatting
on: [push, pull_request]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: '100'
- uses: actions/setup-python@v4
- name: Check commit message formatting
run: source tools/ci.sh && ci_commit_formatting_run

Wyświetl plik

@ -1,16 +0,0 @@
name: Package tests
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- name: Setup environment
run: source tools/ci.sh && ci_package_tests_setup_micropython
- name: Setup libraries
run: source tools/ci.sh && ci_package_tests_setup_lib
- name: Run tests
run: source tools/ci.sh && ci_package_tests_run

Wyświetl plik

@ -1,12 +0,0 @@
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
name: Python code lint and formatting with ruff
on: [push, pull_request]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Version should be kept in sync with .pre-commit_config.yaml & also micropython
- run: pip install --user ruff==0.11.6
- run: ruff check --output-format=github .
- run: ruff format --diff .

Wyświetl plik

@ -1,15 +0,0 @@
repos:
- repo: local
hooks:
- id: verifygitlog
name: MicroPython git commit message format checker
entry: tools/verifygitlog.py --check-file --ignore-rebase
language: python
verbose: true
stages: [commit-msg]
- repo: https://github.com/charliermarsh/ruff-pre-commit
# Version should be kept in sync with .github/workflows/ruff.yml & also micropython
rev: v0.11.6
hooks:
- id: ruff
id: ruff-format

Wyświetl plik

@ -1 +0,0 @@
Please see the [MicroPython Code of Conduct](https://github.com/micropython/micropython/blob/master/CODEOFCONDUCT.md).

Wyświetl plik

@ -1,132 +1,3 @@
## Contributor's Guidelines & Code Conventions
If you submit a pull request, please adhere to Contributor Guidelines:
micropython-lib follows the same general conventions as the [main MicroPython
repository](https://github.com/micropython/micropython). Please see
[micropython/CONTRIBUTING.md](https://github.com/micropython/micropython/blob/master/CONTRIBUTING.md)
and [micropython/CODECONVENTIONS.md](https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md).
### Raising issues
Please include enough information for someone to reproduce the issue you are
describing. This will typically include:
* The version of MicroPython you are using (e.g. the firmware filename, git
hash, or version info printed by the startup message).
* What board/device you are running MicroPython on.
* Which package you have installed, how you installed it, and what version.
When installed via `mip`, all packages will have a `__version__`
attribute.
* A simple code snippet that demonstrates the issue.
If you have a how-to question or are looking for help with using MicroPython
or packages from micropython-lib, please post at the
[discussion forum](https://github.com/orgs/micropython/discussions) instead.
### Pull requests
The same rules for commit messages, signing-off commits, and commit structure
apply [as for the main MicroPython repository](https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md).
All Python code is formatted using the [black](https://github.com/psf/black)
tool. You can run [`tools/codeformat.py`](tools/codeformat.py) to apply
`black` automatically before submitting a PR. The GitHub CI will also run the
[ruff](https://github.com/astral-sh/ruff) tool to apply further "linting"
checks.
Similar to the main repository, a configuration is provided for the
[pre-commit](https://pre-commit.com/) tool to apply `black` code formatting
rules and run `ruff` automatically. See the documentation for using pre-commit
in [the code conventions document](https://github.com/micropython/micropython/blob/master/CODECONVENTIONS.md#automatic-pre-commit-hooks)
In addition to the conventions from the main repository, there are some
specific conventions and guidelines for micropython-lib:
* The first line of the commit message should start with the name of the
package, followed by a short description of the commit. Package names are
globally unique in the micropython-lib directory structure.
For example: `shutil: Add disk_usage function.`
* Although we encourage keeping the code short and minimal, please still use
comments in your code. Typically, packages will be installed via
`mip` and so they will be compiled to bytecode where comments will
_not_ contribute to the installed size.
* All packages must include a `manifest.py`, including a `metadata()` line
with at least a description and a version.
* Prefer to break larger packages up into smaller chunks, so that just the
required functionality can be installed. The way to do this is to have a
base package, e.g. `mypackage` containing `mypackage/__init__.py`, and then
an "extension" package, e.g. `mypackage-ext` containing additional files
e.g. `mypackage/ext.py`. See
[`collections-defaultdict`](python-stdlib/collections-defaultdict) as an
example.
* If you think a package might be extended in this way in the future, prefer
to create a package directory with `package/__init__.py`, rather than a
single `module.py`.
* Packages in the python-stdlib directory should be CPython compatible and
implement a subset of the CPython equivalent. Avoid adding
MicroPython-specific extensions. Please include a link to the corresponding
CPython docs in the PR.
* Include tests (ideally using the `unittest` package) as `test_*.py`.
Otherwise, provide examples as `example_*.py`. When porting CPython
packages, prefer to use the existing tests rather than writing new ones
from scratch.
* When porting an existing third-party package, please ensure that the source
license is compatible.
* To make it easier for others to install packages directly from your PR before
it is merged, consider opting-in to automatic package publishing (see
[Publishing packages from forks](#publishing-packages-from-forks)). If you do
this, consider quoting the [commands to install
packages](README.md#installing-packages-from-forks) in your Pull Request
description.
### Publishing packages from forks
You can easily publish the packages from your micropython-lib
[fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks)
by opting in to a system based on [GitHub
Actions](https://docs.github.com/en/actions) and [GitHub
Pages](https://docs.github.com/en/pages):
1. Open your fork's repository in the GitHub web interface.
2. Navigate to "Settings" -> "Secrets and variables" -> "Actions" -> "Variables".
3. Click "New repository variable"
4. Create a variable named `MICROPY_PUBLISH_MIP_INDEX` with value `true` (or any
"truthy" value).
5. The settings for GitHub Actions and GitHub Pages features should not need to
be changed from the repository defaults, unless you've explicitly disabled
Actions or Pages in your fork.
The next time you push commits to a branch in your fork, GitHub Actions will run
an additional step in the "Build All Packages" workflow named "Publish Packages
for branch". This step runs in *your fork*, but if you open a pull request then
this workflow is not shown in the Pull Request's "Checks". These run in the
upstream repository. Navigate to your fork's Actions tab in order to see
the additional "Publish Packages for branch" step.
Anyone can then install these packages as described under [Installing packages
from forks](README.md#installing-packages-from-forks).
The exact command is also quoted in the GitHub Actions log in your fork's
Actions for the "Publish Packages for branch" step of "Build All Packages".
#### Opting Back Out
To opt-out again, delete the `MICROPY_PUBLISH_MIP_INDEX` variable and
(optionally) delete the `gh-pages` branch from your fork.
*Note*: While enabled, all micropython-lib packages will be published each time
a change is pushed to any branch in your fork. A commit is added to the
`gh-pages` branch each time. In a busy repository, the `gh-pages` branch may
become quite large. The actual `.git` directory size on disk should still be
quite small, as most of the content will be duplicated. If you're worried that
the `gh-pages` branch has become too large then you can always delete this
branch from GitHub. GitHub Actions will create a new `gh-pages` branch the next
time you push a change.
https://github.com/micropython/micropython-lib/wiki/ContributorGuidelines

Wyświetl plik

@ -1,8 +1,8 @@
micropython-lib consists of multiple modules from different sources and
authors. Each module comes under its own licensing terms. The short name of
a license can be found in a file within the module directory (usually
metadata.txt or setup.py). The complete text of each license used is provided
below. Files not belonging to a particular module are provided under the MIT
authors. Each module comes under its own licensing terms. Short name of
a license can be found in a file within a module directory (usually
metadata.txt or setup.py). Complete text of each license used is provided
below. Files not belonging to a particular module a provided under MIT
license, unless explicitly stated otherwise.
=============== MIT License ===============

16
Makefile 100644
Wyświetl plik

@ -0,0 +1,16 @@
PREFIX = ~/.micropython/lib
all:
# Installs all modules to a lib location, for development testing
CMD="find . -maxdepth 1 -mindepth 1 \( -name '*.py' -not -name 'test_*' -not -name 'setup.py' \) -or \( -type d -not -name 'dist' -not -name '*.egg-info' -not -name '__pycache__' \)| xargs --no-run-if-empty cp -r -t $(PREFIX)"
install:
@mkdir -p $(PREFIX)
@if [ -n "$(MOD)" ]; then \
(cd $(MOD); sh -c $(CMD)); \
else \
for d in $$(find -maxdepth 1 -type d ! -name ".*"); do \
echo $$d; \
(cd $$d; sh -c $(CMD)); \
done \
fi

206
README.md
Wyświetl plik

@ -1,172 +1,66 @@
# micropython-lib
~~~~
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
micropython-lib is a highly experimental community project.
This is a repository of packages designed to be useful for writing MicroPython
applications.
Please help to drive it to just "experimental" state by testing
provided packages with MicroPython.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
~~~~
The packages here fall into categories corresponding to the four top-level
directories:
micropython-lib
===============
micropython-lib is a project to develop a non-monolothic standard library
for MicroPython. Each module or package is available as a separate
distribution package from PyPI. Each module is either written from scratch or
ported from CPython.
* **python-stdlib**: Compatible versions of modules from [The Python Standard
Library](https://docs.python.org/3/library/). These should be drop-in
replacements for the corresponding Python modules, although many have
reduced functionality or missing methods or classes (which may not be an
issue for most cases).
Note that the main target of micropython-lib is a "Unix" port of MicroPython
(additional ports to support are to be determined). Actual system requirements
vary per module. Though if a module is not related to I/O, the module should
work without problem on bare-metal ports too (e.g. pyboard).
* **python-ecosys**: Compatible, but reduced-functionality versions of
packages from the wider Python ecosystem. For example, a package that
might be found in the [Python Package Index](https://pypi.org/).
* **micropython**: MicroPython-specific packages that do not have equivalents
in other Python environments. This includes drivers for hardware
(e.g. sensors, peripherals, or displays), libraries to work with
embedded functionality (e.g. bluetooth), or MicroPython-specific
packages that do not have equivalents in CPython.
Usage
-----
micropython-lib packages are published on PyPI (Python Package Index),
the standard Python community package repository: http://pypi.python.org/ .
On PyPi, you can search for MicroPython related packages and read
additional package information.
* **unix-ffi**: These packages are specifically for the MicroPython Unix port
and provide access to operating-system and third-party libraries via FFI,
or functionality that is not useful for non-Unix ports.
To install packages from PyPI for usage on your local system, use the
`pip-micropython` tool, which is a simple wrapper around the standard
`pip` tool, which is used to install packages for CPython.
The `pip-micropython` tool can be found in `tools` subdirectory
of the main MicroPython repository (https://github.com/micropython/micropython).
Just install the `pip-micropython` script somewhere on your `PATH`.
## Usage
Afterwards, just use `pip-micropython` in a way similar to `pip`:
To install a micropython-lib package, there are four main options. For more
information see the [Package management documentation](https://docs.micropython.org/en/latest/reference/packages.html)
documentation.
~~~~
$ pip-micropython install micropython-copy
$ micropython
>>> import copy
>>> copy.copy([1, 2, 3])
[1, 2, 3]
~~~~
### On a network-enabled device
Review the `pip-micropython` source code for more info.
As of MicroPython v1.20 (and nightly builds since October 2022), boards
with WiFi and Ethernet support include the `mip` package manager.
```py
>>> import mip
>>> mip.install("package-name")
```
Development
-----------
To install modules during development, use `make install`. By default, all
available packages will be installed. To install a specific module, add the
`MOD=<module>` parameter to the end of the `make install` command.
### Using `mpremote` from your PC
`mpremote` is the officially-supported tool for interacting with a MicroPython
device and, since v0.4.0, support for installing micropython-lib packages is
provided by using the `mip` command.
Links
-----
More information is on GitHub and in the MicroPython forums:
```bash
$ mpremote connect /dev/ttyUSB0 mip install package-name
```
* https://github.com/micropython/micropython/issues/405
* http://forum.micropython.org/viewtopic.php?f=5&t=70
See the [mpremote documentation](https://docs.micropython.org/en/latest/reference/mpremote.html).
### Freeze into your firmware
If you are building your own firmware, all packages in this repository include
a `manifest.py` that can be included into your board manifest via the
`require()` command. See [Manifest files](https://docs.micropython.org/en/latest/reference/manifest.html#require) for
more information.
### Copy the files manually
Many micropython-lib packages are just single-file modules, and you can
quickly get started by copying the relevant Python file to your device. For
example, to add the `base64` library, you can directly copy
`python-stdlib/base64/base64.py` to the `lib` directory on your device.
This can be done using `mpremote`, for example:
```bash
$ mpremote connect /dev/ttyUSB0 cp python-stdlib/base64/base64.py :/lib
```
For packages that are implemented as a package directory, you'll need to copy
the directory instead. For example, to add `collections.defaultdict`, copy
`collections/collections/__init__.py` and
`collections-defaultdict/collections/defaultdict.py` to a directory named
`lib/collections` on your device.
Note that unlike the other three approaches based on `mip` or `manifest.py`,
you will need to manually resolve dependencies. You can inspect the relevant
`manifest.py` file to view the list of dependencies for a given package.
## Installing packages from forks
It is possible to use the `mpremote mip install` or `mip.install()` methods to
install packages built from a
[fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks)
of micropython-lib, if the fork's owner has opted in.
This can be useful to install packages from a pending Pull Request, for example.
First, the owner of the fork must opt-in as described under
[Publishing packages from forks](CONTRIBUTING.md#publishing-packages-from-forks).
After this has happened, each time someone pushes to a branch in that fork then
GitHub Actions will automatically publish the packages to a GitHub Pages site.
To install these packages, use commands such as:
```bash
$ mpremote connect /dev/ttyUSB0 mip install --index https://USERNAME.github.io/micropython-lib/mip/BRANCH_NAME PACKAGE_NAME
```
Or from a networked device:
```py
import mip
mip.install(PACKAGE_NAME, index="https://USERNAME.github.io/micropython-lib/mip/BRANCH_NAME")
```
(Where `USERNAME`, `BRANCH_NAME` and `PACKAGE_NAME` are replaced with the owner
of the fork, the branch the packages were built from, and the package name.)
## Contributing
We use [GitHub Discussions](https://github.com/micropython/micropython/discussions)
as our forum, and [Discord](https://micropython.org/discord) for chat. These
are great places to ask questions and advice from the community or to discuss your
MicroPython-based projects.
The [MicroPython Wiki](https://github.com/micropython/micropython/wiki) is
also used for micropython-lib.
For bugs and feature requests, please [raise an issue](https://github.com/micropython/micropython-lib/issues/new).
We welcome pull requests to add new packages, fix bugs, or add features.
Please be sure to follow the
[Contributor's Guidelines & Code Conventions](CONTRIBUTING.md). Note that
MicroPython is licensed under the [MIT license](LICENSE) and all contributions
should follow this license.
### Future plans (and new contributor ideas)
* Develop a set of example programs using these packages.
* Develop more MicroPython packages for common tasks.
* Expand unit testing coverage.
* Add support for referencing remote/third-party repositories.
## Notes on terminology
The terms *library*, *package*, and *module* are overloaded and lead to some
confusion. The interpretation used in by the MicroPython project is that:
A *library* is a collection of installable packages, e.g. [The Python Standard
Library](https://docs.python.org/3/library/), or micropython-lib.
A *package* can refer to two things. The first meaning, "library package", is
something that can be installed from a library, e.g. via `mip` (or `pip` in
CPython/PyPI). Packages provide *modules* that can be imported. The ambiguity
here is that the module provided by the package does not necessarily have to
have the same name, e.g. the `pyjwt` package provides the `jwt` module. In
CPython, the `pyserial` package providing the `serial` module is another
common example.
A *module* is something that can be imported. For example, "the *os* module".
A module can be implemented either as a single file, typically also called
a *module* or "single-file module", or as a *package* (the second meaning),
which in this context means a directory containing multiple `.py` files
(usually at least an `__init__.py`).
In micropython-lib, we also have the concept of an *extension package* which
is a library package that extends the functionality of another package, by
adding additional files to the same package directory. These packages have
hyphenated names. For example, the `collections-defaultdict` package extends
the `collections` package to add the `defaultdict` class to the `collections`
module.
Guidelines for packaging MicroPython modules for PyPI:
* https://github.com/micropython/micropython/issues/413

Wyświetl plik

@ -5,4 +5,3 @@ absolute_import = True
with_statement = True
print_function = True
unicode_literals = True
annotations = True

Wyświetl plik

@ -0,0 +1,4 @@
srctype=dummy
type=module
version=0.0.2
dist_name=future

Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-future',
version='0.0.2',
description='Dummy __future__ module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['__future__'])

Wyświetl plik

@ -4,8 +4,7 @@ import sys
_h = None
names = ("libc.so", "libc.so.0", "libc.so.6", "libc.dylib")
names = ('libc.so', 'libc.so.0', 'libc.so.6', 'libc.dylib')
def get():
global _h
@ -25,7 +24,6 @@ def set_names(n):
global names
names = n
# Find out bitness of the platform, even if long ints are not supported
# TODO: All bitness differences should be removed from micropython-lib, and
# this snippet too.

Wyświetl plik

@ -0,0 +1,7 @@
dist_name = libc
srctype = micropython-lib
type = module
version = 0.3
author = Paul Sokolovsky
desc = MicroPython FFI helper module (deprecated)
long_desc = MicroPython FFI helper module (deprecated, replaced by micropython-ffilib).

18
_libc/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-libc',
version='0.3',
description='MicroPython FFI helper module (deprecated)',
long_description='MicroPython FFI helper module (deprecated, replaced by micropython-ffilib).',
url='https://github.com/micropython/micropython/issues/405',
author='Paul Sokolovsky',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['_libc'])

Wyświetl plik

@ -7,15 +7,15 @@ documented public API and should not be used directly.
import re
_declname_match = re.compile(r"[a-zA-Z][-_.a-zA-Z0-9]*\s*").match
_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9]*\s*').match
_declstringlit_match = re.compile(r'(\'[^\']*\'|"[^"]*")\s*').match
_commentclose = re.compile(r"--\s*>")
_markedsectionclose = re.compile(r"]\s*]\s*>")
_commentclose = re.compile(r'--\s*>')
_markedsectionclose = re.compile(r']\s*]\s*>')
# An analysis of the MS-Word extensions is available at
# http://www.planetpublish.com/xmlarena/xap/Thursday/WordtoXML.pdf
_msmarkedsectionclose = re.compile(r"]\s*>")
_msmarkedsectionclose = re.compile(r']\s*>')
del re
@ -26,10 +26,12 @@ class ParserBase:
def __init__(self):
if self.__class__ is ParserBase:
raise RuntimeError("_markupbase.ParserBase must be subclassed")
raise RuntimeError(
"_markupbase.ParserBase must be subclassed")
def error(self, message):
raise NotImplementedError("subclasses of ParserBase must override error()")
raise NotImplementedError(
"subclasses of ParserBase must override error()")
def reset(self):
self.lineno = 1
@ -50,13 +52,13 @@ class ParserBase:
nlines = rawdata.count("\n", i, j)
if nlines:
self.lineno = self.lineno + nlines
pos = rawdata.rindex("\n", i, j) # Should not fail
self.offset = j - (pos + 1)
pos = rawdata.rindex("\n", i, j) # Should not fail
self.offset = j-(pos+1)
else:
self.offset = self.offset + j - i
self.offset = self.offset + j-i
return j
_decl_otherchars = ""
_decl_otherchars = ''
# Internal -- parse declaration (for use by subclasses).
def parse_declaration(self, i):
@ -73,35 +75,35 @@ class ParserBase:
rawdata = self.rawdata
j = i + 2
assert rawdata[i:j] == "<!", "unexpected call to parse_declaration"
if rawdata[j : j + 1] == ">":
if rawdata[j:j+1] == ">":
# the empty comment <!>
return j + 1
if rawdata[j : j + 1] in ("-", ""):
if rawdata[j:j+1] in ("-", ""):
# Start of comment followed by buffer boundary,
# or just a buffer boundary.
return -1
# A simple, practical version could look like: ((name|stringlit) S*) + '>'
n = len(rawdata)
if rawdata[j : j + 2] == "--": # comment
if rawdata[j:j+2] == '--': #comment
# Locate --.*-- as the body of the comment
return self.parse_comment(i)
elif rawdata[j] == "[": # marked section
elif rawdata[j] == '[': #marked section
# Locate [statusWord [...arbitrary SGML...]] as the body of the marked section
# Where statusWord is one of TEMP, CDATA, IGNORE, INCLUDE, RCDATA
# Note that this is extended by Microsoft Office "Save as Web" function
# to include [if...] and [endif].
return self.parse_marked_section(i)
else: # all other declaration elements
else: #all other declaration elements
decltype, j = self._scan_name(j, i)
if j < 0:
return j
if decltype == "doctype":
self._decl_otherchars = ""
self._decl_otherchars = ''
while j < n:
c = rawdata[j]
if c == ">":
# end of declaration syntax
data = rawdata[i + 2 : j]
data = rawdata[i+2:j]
if decltype == "doctype":
self.handle_decl(data)
else:
@ -114,7 +116,7 @@ class ParserBase:
if c in "\"'":
m = _declstringlit_match(rawdata, j)
if not m:
return -1 # incomplete
return -1 # incomplete
j = m.end()
elif c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
name, j = self._scan_name(j, i)
@ -133,45 +135,46 @@ class ParserBase:
else:
self.error("unexpected '[' char in declaration")
else:
self.error("unexpected %r char in declaration" % rawdata[j])
self.error(
"unexpected %r char in declaration" % rawdata[j])
if j < 0:
return j
return -1 # incomplete
return -1 # incomplete
# Internal -- parse a marked section
# Override this to handle MS-word extension syntax <![if word]>content<![endif]>
def parse_marked_section(self, i, report=1):
rawdata = self.rawdata
assert rawdata[i : i + 3] == "<![", "unexpected call to parse_marked_section()"
sectName, j = self._scan_name(i + 3, i)
rawdata= self.rawdata
assert rawdata[i:i+3] == '<![', "unexpected call to parse_marked_section()"
sectName, j = self._scan_name( i+3, i )
if j < 0:
return j
if sectName in {"temp", "cdata", "ignore", "include", "rcdata"}:
# look for standard ]]> ending
match = _markedsectionclose.search(rawdata, i + 3)
match= _markedsectionclose.search(rawdata, i+3)
elif sectName in {"if", "else", "endif"}:
# look for MS Office ]> ending
match = _msmarkedsectionclose.search(rawdata, i + 3)
match= _msmarkedsectionclose.search(rawdata, i+3)
else:
self.error("unknown status keyword %r in marked section" % rawdata[i + 3 : j])
self.error('unknown status keyword %r in marked section' % rawdata[i+3:j])
if not match:
return -1
if report:
j = match.start(0)
self.unknown_decl(rawdata[i + 3 : j])
self.unknown_decl(rawdata[i+3: j])
return match.end(0)
# Internal -- parse comment, return length or -1 if not terminated
def parse_comment(self, i, report=1):
rawdata = self.rawdata
if rawdata[i : i + 4] != "<!--":
self.error("unexpected call to parse_comment()")
match = _commentclose.search(rawdata, i + 4)
if rawdata[i:i+4] != '<!--':
self.error('unexpected call to parse_comment()')
match = _commentclose.search(rawdata, i+4)
if not match:
return -1
if report:
j = match.start(0)
self.handle_comment(rawdata[i + 4 : j])
self.handle_comment(rawdata[i+4: j])
return match.end(0)
# Internal -- scan past the internal subset in a <!DOCTYPE declaration,
@ -183,7 +186,7 @@ class ParserBase:
while j < n:
c = rawdata[j]
if c == "<":
s = rawdata[j : j + 2]
s = rawdata[j:j+2]
if s == "<":
# end of buffer; incomplete
return -1
@ -196,7 +199,7 @@ class ParserBase:
if (j + 4) > n:
# end of buffer; incomplete
return -1
if rawdata[j : j + 4] == "<!--":
if rawdata[j:j+4] == "<!--":
j = self.parse_comment(j, report=0)
if j < 0:
return j
@ -206,7 +209,8 @@ class ParserBase:
return -1
if name not in {"attlist", "element", "entity", "notation"}:
self.updatepos(declstartpos, j + 2)
self.error("unknown declaration %r in internal subset" % name)
self.error(
"unknown declaration %r in internal subset" % name)
# handle the individual names
meth = getattr(self, "_parse_doctype_" + name)
j = meth(j, declstartpos)
@ -248,7 +252,7 @@ class ParserBase:
return -1
# style content model; just skip until '>'
rawdata = self.rawdata
if ">" in rawdata[j:]:
if '>' in rawdata[j:]:
return rawdata.find(">", j) + 1
return -1
@ -256,7 +260,7 @@ class ParserBase:
def _parse_doctype_attlist(self, i, declstartpos):
rawdata = self.rawdata
name, j = self._scan_name(i, declstartpos)
c = rawdata[j : j + 1]
c = rawdata[j:j+1]
if c == "":
return -1
if c == ">":
@ -267,7 +271,7 @@ class ParserBase:
name, j = self._scan_name(j, declstartpos)
if j < 0:
return j
c = rawdata[j : j + 1]
c = rawdata[j:j+1]
if c == "":
return -1
if c == "(":
@ -276,14 +280,14 @@ class ParserBase:
j = rawdata.find(")", j) + 1
else:
return -1
while rawdata[j : j + 1].isspace():
while rawdata[j:j+1].isspace():
j = j + 1
if not rawdata[j:]:
# end of buffer, incomplete
return -1
else:
name, j = self._scan_name(j, declstartpos)
c = rawdata[j : j + 1]
c = rawdata[j:j+1]
if not c:
return -1
if c in "'\"":
@ -292,7 +296,7 @@ class ParserBase:
j = m.end()
else:
return -1
c = rawdata[j : j + 1]
c = rawdata[j:j+1]
if not c:
return -1
if c == "#":
@ -302,10 +306,10 @@ class ParserBase:
name, j = self._scan_name(j + 1, declstartpos)
if j < 0:
return j
c = rawdata[j : j + 1]
c = rawdata[j:j+1]
if not c:
return -1
if c == ">":
if c == '>':
# all done
return j + 1
@ -316,11 +320,11 @@ class ParserBase:
return j
rawdata = self.rawdata
while 1:
c = rawdata[j : j + 1]
c = rawdata[j:j+1]
if not c:
# end of buffer; incomplete
return -1
if c == ">":
if c == '>':
return j + 1
if c in "'\"":
m = _declstringlit_match(rawdata, j)
@ -335,10 +339,10 @@ class ParserBase:
# Internal -- scan past <!ENTITY declarations
def _parse_doctype_entity(self, i, declstartpos):
rawdata = self.rawdata
if rawdata[i : i + 1] == "%":
if rawdata[i:i+1] == "%":
j = i + 1
while 1:
c = rawdata[j : j + 1]
c = rawdata[j:j+1]
if not c:
return -1
if c.isspace():
@ -351,7 +355,7 @@ class ParserBase:
if j < 0:
return j
while 1:
c = self.rawdata[j : j + 1]
c = self.rawdata[j:j+1]
if not c:
return -1
if c in "'\"":
@ -359,7 +363,7 @@ class ParserBase:
if m:
j = m.end()
else:
return -1 # incomplete
return -1 # incomplete
elif c == ">":
return j + 1
else:
@ -383,7 +387,8 @@ class ParserBase:
return name.lower(), m.end()
else:
self.updatepos(declstartpos, i)
self.error("expected name token at %r" % rawdata[declstartpos : declstartpos + 20])
self.error("expected name token at %r"
% rawdata[declstartpos:declstartpos+20])
# To be overridden -- handlers for unknown objects
def unknown_decl(self, data):

Wyświetl plik

@ -0,0 +1,4 @@
srctype = cpython
type = module
version = 3.3.3
depends = re-pcre

Wyświetl plik

@ -0,0 +1,19 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-_markupbase',
version='3.3.3',
description='CPython _markupbase module ported to MicroPython',
long_description='This is a module ported from CPython standard library to be compatible with\nMicroPython interpreter. Usually, this means applying small patches for\nfeatures not supported (yet, or at all) in MicroPython. Sometimes, heavier\nchanges are required. Note that CPython modules are written with availability\nof vast resources in mind, and may not work for MicroPython ports with\nlimited heap. If you are affected by such a case, please help reimplement\nthe module from scratch.',
url='https://github.com/micropython/micropython/issues/405',
author='CPython Developers',
author_email='python-dev@python.org',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='Python',
py_modules=['_markupbase'],
install_requires=['micropython-re-pcre'])

Wyświetl plik

@ -1,6 +1,2 @@
class ABC:
pass
def abstractmethod(f):
return f

3
abc/metadata.txt 100644
Wyświetl plik

@ -0,0 +1,3 @@
srctype=dummy
type=module
version=0.0.0

18
abc/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-abc',
version='0.0.0',
description='Dummy abc module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['abc'])

Wyświetl plik

@ -3,7 +3,7 @@ Minimal and functional version of CPython's argparse module.
"""
import sys
from collections import namedtuple
from ucollections import namedtuple
class _ArgError(BaseException):
@ -104,16 +104,8 @@ class ArgumentParser:
if not args:
args = [dest]
list.append(
_Arg(
args,
dest,
action,
kwargs.get("nargs", None),
const,
default,
kwargs.get("help", ""),
)
)
_Arg(args, dest, action, kwargs.get("nargs", None),
const, default, kwargs.get("help", "")))
def usage(self, full):
# print short usage
@ -129,9 +121,8 @@ class ArgumentParser:
return " %s%s" % (arg.dest, arg.nargs)
else:
return ""
for opt in self.opt:
print(" [%s%s]" % (", ".join(opt.names), render_arg(opt)), end="")
print(" [%s%s]" % (', '.join(opt.names), render_arg(opt)), end="")
for pos in self.pos:
print(render_arg(pos), end="")
print()
@ -150,27 +141,21 @@ class ArgumentParser:
print("\noptional args:")
print(" -h, --help show this message and exit")
for opt in self.opt:
print(" %-16s%s" % (", ".join(opt.names) + render_arg(opt), opt.help))
print(" %-16s%s" % (', '.join(opt.names) + render_arg(opt), opt.help))
def parse_args(self, args=None):
return self._parse_args_impl(args, False)
def parse_known_args(self, args=None):
return self._parse_args_impl(args, True)
def _parse_args_impl(self, args, return_unknown):
if args is None:
args = sys.argv[1:]
else:
args = args[:]
try:
return self._parse_args(args, return_unknown)
return self._parse_args(args)
except _ArgError as e:
self.usage(False)
print("error:", e)
sys.exit(2)
def _parse_args(self, args, return_unknown):
def _parse_args(self, args):
# add optional args with defaults
arg_dest = []
arg_vals = []
@ -178,13 +163,6 @@ class ArgumentParser:
arg_dest.append(opt.dest)
arg_vals.append(opt.default)
# deal with unknown arguments, if needed
unknown = []
def consume_unknown():
while args and not args[0].startswith("-"):
unknown.append(args.pop(0))
# parse all args
parsed_pos = False
while args or not parsed_pos:
@ -201,26 +179,15 @@ class ArgumentParser:
found = True
break
if not found:
if return_unknown:
unknown.append(a)
consume_unknown()
else:
raise _ArgError("unknown option %s" % a)
raise _ArgError("unknown option %s" % a)
else:
# positional arg
if parsed_pos:
if return_unknown:
unknown = unknown + args
break
else:
raise _ArgError("extra args: %s" % " ".join(args))
raise _ArgError("extra args: %s" % " ".join(args))
for pos in self.pos:
arg_dest.append(pos.dest)
arg_vals.append(pos.parse(pos.names[0], args))
parsed_pos = True
if return_unknown:
consume_unknown()
# build and return named tuple with arg values
values = namedtuple("args", arg_dest)(*arg_vals)
return (values, unknown) if return_unknown else values
return namedtuple("args", arg_dest)(*arg_vals)

Wyświetl plik

@ -0,0 +1,4 @@
srctype = micropython-lib
type = module
version = 0.3.2
author = Damien George

18
argparse/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-argparse',
version='0.3.2',
description='argparse module for MicroPython',
long_description="This is a module reimplemented specifically for MicroPython standard library,\nwith efficient and lean design in mind. Note that this module is likely work\nin progress and likely supports just a subset of CPython's corresponding\nmodule. Please help with the development if you are interested in this\nmodule.",
url='https://github.com/micropython/micropython/issues/405',
author='Damien George',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['argparse'])

Wyświetl plik

@ -44,25 +44,3 @@ args = parser.parse_args(["a", "b"])
assert args.files1 == ["a", "b"] and args.files2 == []
args = parser.parse_args(["a", "b", "c"])
assert args.files1 == ["a", "b"] and args.files2 == ["c"]
parser = argparse.ArgumentParser()
parser.add_argument("a", nargs=2)
parser.add_argument("-b")
args, rest = parser.parse_known_args(["a", "b", "-b", "2"])
assert args.a == ["a", "b"] and args.b == "2"
assert rest == []
args, rest = parser.parse_known_args(["-b", "2", "a", "b", "c"])
assert args.a == ["a", "b"] and args.b == "2"
assert rest == ["c"]
args, rest = parser.parse_known_args(["a", "b", "-b", "2", "c"])
assert args.a == ["a", "b"] and args.b == "2"
assert rest == ["c"]
args, rest = parser.parse_known_args(["-b", "2", "a", "b", "-", "c"])
assert args.a == ["a", "b"] and args.b == "2"
assert rest == ["-", "c"]
args, rest = parser.parse_known_args(["a", "b", "-b", "2", "-", "x", "y"])
assert args.a == ["a", "b"] and args.b == "2"
assert rest == ["-", "x", "y"]
args, rest = parser.parse_known_args(["a", "b", "c", "-b", "2", "--x", "5", "1"])
assert args.a == ["a", "b"] and args.b == "2"
assert rest == ["c", "--x", "5", "1"]

Wyświetl plik

@ -0,0 +1,151 @@
import time
import logging
log = logging.getLogger("asyncio")
# Workaround for not being able to subclass builtin types
class LoopStop(Exception):
pass
class InvalidStateError(Exception):
pass
# Object not matching any other object
_sentinel = []
class EventLoop:
def __init__(self):
self.q = []
def call_soon(self, c, *args):
self.q.append((c, args))
def call_later(self, delay, c, *args):
def _delayed(c, args, delay):
yield from sleep(delay)
self.call_soon(c, *args)
Task(_delayed(c, args, delay))
def run_forever(self):
while self.q:
c = self.q.pop(0)
try:
c[0](*c[1])
except LoopStop:
return
# I mean, forever
while True:
time.sleep(1)
def stop(self):
def _cb():
raise LoopStop
self.call_soon(_cb)
def run_until_complete(self, coro):
t = async(coro)
t.add_done_callback(lambda a: self.stop())
self.run_forever()
def close(self):
pass
_def_event_loop = EventLoop()
class Future:
def __init__(self, loop=_def_event_loop):
self.loop = loop
self.res = _sentinel
self.cbs = []
def result(self):
if self.res is _sentinel:
raise InvalidStateError
return self.res
def add_done_callback(self, fn):
if self.res is _sentinel:
self.cbs.append(fn)
else:
self.loop.call_soon(fn, self)
def set_result(self, val):
self.res = val
for f in self.cbs:
f(self)
class Task(Future):
def __init__(self, coro, loop=_def_event_loop):
super().__init__()
self.loop = loop
self.c = coro
# upstream asyncio forces task to be scheduled on instantiation
self.loop.call_soon(self)
def __call__(self):
try:
next(self.c)
self.loop.call_soon(self)
except StopIteration as e:
log.debug("Coro finished: %s", self.c)
self.set_result(None)
def get_event_loop():
return _def_event_loop
# Decorator
def coroutine(f):
return f
def async(coro):
if isinstance(coro, Future):
return coro
return Task(coro)
class _Wait(Future):
def __init__(self, n):
Future.__init__(self)
self.n = n
def _done(self):
self.n -= 1
log.debug("Wait: remaining tasks: %d", self.n)
if not self.n:
self.set_result(None)
def __call__(self):
pass
def wait(coro_list, loop=_def_event_loop):
w = _Wait(len(coro_list))
for c in coro_list:
t = async(c)
t.add_done_callback(lambda val: w._done())
return w
def sleep(secs):
t = time.time()
log.debug("Started sleep at: %s, targetting: %s", t, t + secs)
while time.time() < t + secs:
time.sleep(0.01)
yield
log.debug("Finished sleeping %ss", secs)

Wyświetl plik

@ -0,0 +1,18 @@
#https://docs.python.org/3.4/library/asyncio-task.html#example-chain-coroutines
#import asyncio
import asyncio_slow as asyncio
@asyncio.coroutine
def compute(x, y):
print("Compute %s + %s ..." % (x, y))
yield from asyncio.sleep(1.0)
return x + y
@asyncio.coroutine
def print_sum(x, y):
result = yield from compute(x, y)
print("%s + %s = %s" % (x, y, result))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()

Wyświetl plik

@ -0,0 +1,15 @@
#https://docs.python.org/3.4/library/asyncio-task.html#example-chain-coroutines
#import asyncio
import asyncio_slow as asyncio
@asyncio.coroutine
def slow_operation(future):
yield from asyncio.sleep(1)
future.set_result('Future is done!')
loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.Task(slow_operation(future))
loop.run_until_complete(future)
print(future.result())
loop.close()

Wyświetl plik

@ -0,0 +1,21 @@
#https://docs.python.org/3.4/library/asyncio-task.html#example-future-with-run-forever
#import asyncio
import asyncio_slow as asyncio
@asyncio.coroutine
def slow_operation(future):
yield from asyncio.sleep(1)
future.set_result('Future is done!')
def got_result(future):
print(future.result())
loop.stop()
loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.Task(slow_operation(future))
future.add_done_callback(got_result)
try:
loop.run_forever()
finally:
loop.close()

Wyświetl plik

@ -0,0 +1,12 @@
#https://docs.python.org/3.4/library/asyncio-task.html#example-hello-world-coroutine
#import asyncio
import asyncio_slow as asyncio
@asyncio.coroutine
def greet_every_two_seconds():
while True:
print('Hello World')
yield from asyncio.sleep(2)
loop = asyncio.get_event_loop()
loop.run_until_complete(greet_every_two_seconds())

Wyświetl plik

@ -0,0 +1,12 @@
#import asyncio
import asyncio_slow as asyncio
@asyncio.coroutine
def greet_every_two_seconds():
while True:
print('Hello World')
yield from asyncio.sleep(2)
loop = asyncio.get_event_loop()
asyncio.Task(greet_every_two_seconds())
loop.run_forever()

Wyświetl plik

@ -0,0 +1,11 @@
# https://docs.python.org/3.4/library/asyncio-eventloop.html#example-hello-world-callback
#import asyncio
import asyncio_slow as asyncio
def print_and_repeat(loop):
print('Hello World')
loop.call_later(2, print_and_repeat, loop)
loop = asyncio.get_event_loop()
loop.call_soon(print_and_repeat, loop)
loop.run_forever()

Wyświetl plik

@ -0,0 +1,21 @@
#https://docs.python.org/3.4/library/asyncio-task.html#example-parallel-execution-of-tasks
#import asyncio
import asyncio_slow as asyncio
@asyncio.coroutine
def factorial(name, number):
f = 1
for i in range(2, number+1):
print("Task %s: Compute factorial(%s)..." % (name, i))
yield from asyncio.sleep(1)
f *= i
print("Task %s: factorial(%s) = %s" % (name, number, f))
tasks = [
asyncio.Task(factorial("A", 2)),
asyncio.Task(factorial("B", 3)),
asyncio.Task(factorial("C", 4))]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

Wyświetl plik

@ -13,67 +13,38 @@ import binascii
__all__ = [
# Legacy interface exports traditional RFC 1521 Base64 encodings
"encode",
"decode",
"encodebytes",
"decodebytes",
'encode', 'decode', 'encodebytes', 'decodebytes',
# Generalized interface for other encodings
"b64encode",
"b64decode",
"b32encode",
"b32decode",
"b16encode",
"b16decode",
'b64encode', 'b64decode', 'b32encode', 'b32decode',
'b16encode', 'b16decode',
# Standard Base64 encoding
"standard_b64encode",
"standard_b64decode",
'standard_b64encode', 'standard_b64decode',
# Some common Base64 alternatives. As referenced by RFC 3458, see thread
# starting at:
#
# http://zgp.org/pipermail/p2p-hackers/2001-September/000316.html
"urlsafe_b64encode",
"urlsafe_b64decode",
]
'urlsafe_b64encode', 'urlsafe_b64decode',
]
bytes_types = (bytes, bytearray) # Types acceptable as binary data
def _bytes_from_decode_data(s):
if isinstance(s, str):
try:
return s.encode("ascii")
# except UnicodeEncodeError:
return s.encode('ascii')
# except UnicodeEncodeError:
except:
raise ValueError("string argument should contain only ASCII characters")
raise ValueError('string argument should contain only ASCII characters')
elif isinstance(s, bytes_types):
return s
else:
raise TypeError("argument should be bytes or ASCII string, not %s" % s.__class__.__name__)
def _maketrans(f, t):
"""Re-implement bytes.maketrans() as there is no such function in micropython"""
if len(f) != len(t):
raise ValueError("maketrans arguments must have same length")
translation_table = dict(zip(f, t))
return translation_table
def _translate(input_bytes, trans_table):
"""Re-implement bytes.translate() as there is no such function in micropython"""
result = bytearray()
for byte in input_bytes:
translated_byte = trans_table.get(byte, byte)
result.append(translated_byte)
return bytes(result)
# Base64 encoding/decoding uses binascii
def b64encode(s, altchars=None):
"""Encode a byte string using Base64.
@ -90,9 +61,10 @@ def b64encode(s, altchars=None):
encoded = binascii.b2a_base64(s)[:-1]
if altchars is not None:
if not isinstance(altchars, bytes_types):
raise TypeError("expected bytes, not %s" % altchars.__class__.__name__)
raise TypeError("expected bytes, not %s"
% altchars.__class__.__name__)
assert len(altchars) == 2, repr(altchars)
encoded = _translate(encoded, _maketrans(b"+/", altchars))
return encoded.translate(bytes.maketrans(b'+/', altchars))
return encoded
@ -114,9 +86,9 @@ def b64decode(s, altchars=None, validate=False):
if altchars is not None:
altchars = _bytes_from_decode_data(altchars)
assert len(altchars) == 2, repr(altchars)
s = _translate(s, _maketrans(altchars, b"+/"))
if validate and not re.match(b"^[A-Za-z0-9+/]*=*$", s):
raise binascii.Error("Non-base64 digit found")
s = s.translate(bytes.maketrans(altchars, b'+/'))
if validate and not re.match(b'^[A-Za-z0-9+/]*={0,2}$', s):
raise binascii.Error('Non-base64 digit found')
return binascii.a2b_base64(s)
@ -127,7 +99,6 @@ def standard_b64encode(s):
"""
return b64encode(s)
def standard_b64decode(s):
"""Decode a byte string encoded with the standard Base64 alphabet.
@ -139,9 +110,8 @@ def standard_b64decode(s):
return b64decode(s)
# _urlsafe_encode_translation = _maketrans(b'+/', b'-_')
# _urlsafe_decode_translation = _maketrans(b'-_', b'+/')
#_urlsafe_encode_translation = bytes.maketrans(b'+/', b'-_')
#_urlsafe_decode_translation = bytes.maketrans(b'-_', b'+/')
def urlsafe_b64encode(s):
"""Encode a byte string using a url-safe Base64 alphabet.
@ -150,9 +120,8 @@ def urlsafe_b64encode(s):
returned. The alphabet uses '-' instead of '+' and '_' instead of
'/'.
"""
# return b64encode(s).translate(_urlsafe_encode_translation)
return b64encode(s, b"-_").rstrip(b"\n")
# return b64encode(s).translate(_urlsafe_encode_translation)
raise NotImplementedError()
def urlsafe_b64decode(s):
"""Decode a byte string encoded with the standard Base64 alphabet.
@ -164,47 +133,25 @@ def urlsafe_b64decode(s):
The alphabet uses '-' instead of '+' and '_' instead of '/'.
"""
# s = _bytes_from_decode_data(s)
# s = s.translate(_urlsafe_decode_translation)
# return b64decode(s)
# s = _bytes_from_decode_data(s)
# s = s.translate(_urlsafe_decode_translation)
# return b64decode(s)
raise NotImplementedError()
# Base32 encoding/decoding must be done in Python
_b32alphabet = {
0: b"A",
9: b"J",
18: b"S",
27: b"3",
1: b"B",
10: b"K",
19: b"T",
28: b"4",
2: b"C",
11: b"L",
20: b"U",
29: b"5",
3: b"D",
12: b"M",
21: b"V",
30: b"6",
4: b"E",
13: b"N",
22: b"W",
31: b"7",
5: b"F",
14: b"O",
23: b"X",
6: b"G",
15: b"P",
24: b"Y",
7: b"H",
16: b"Q",
25: b"Z",
8: b"I",
17: b"R",
26: b"2",
}
0: b'A', 9: b'J', 18: b'S', 27: b'3',
1: b'B', 10: b'K', 19: b'T', 28: b'4',
2: b'C', 11: b'L', 20: b'U', 29: b'5',
3: b'D', 12: b'M', 21: b'V', 30: b'6',
4: b'E', 13: b'N', 22: b'W', 31: b'7',
5: b'F', 14: b'O', 23: b'X',
6: b'G', 15: b'P', 24: b'Y',
7: b'H', 16: b'Q', 25: b'Z',
8: b'I', 17: b'R', 26: b'2',
}
_b32tab = [v[0] for k, v in sorted(_b32alphabet.items())]
_b32rev = dict([(v[0], k) for k, v in _b32alphabet.items()])
@ -229,30 +176,27 @@ def b32encode(s):
# leftover bit of c1 and tack it onto c2. Then we take the 2 leftover
# bits of c2 and tack them onto c3. The shifts and masks are intended
# to give us values of exactly 5 bits in width.
c1, c2, c3 = struct.unpack("!HHB", s[i * 5 : (i + 1) * 5])
c2 += (c1 & 1) << 16 # 17 bits wide
c1, c2, c3 = struct.unpack('!HHB', s[i*5:(i+1)*5])
c2 += (c1 & 1) << 16 # 17 bits wide
c3 += (c2 & 3) << 8 # 10 bits wide
encoded += bytes(
[
_b32tab[c1 >> 11], # bits 1 - 5
_b32tab[(c1 >> 6) & 0x1F], # bits 6 - 10
_b32tab[(c1 >> 1) & 0x1F], # bits 11 - 15
_b32tab[c2 >> 12], # bits 16 - 20 (1 - 5)
_b32tab[(c2 >> 7) & 0x1F], # bits 21 - 25 (6 - 10)
_b32tab[(c2 >> 2) & 0x1F], # bits 26 - 30 (11 - 15)
_b32tab[c3 >> 5], # bits 31 - 35 (1 - 5)
_b32tab[c3 & 0x1F], # bits 36 - 40 (1 - 5)
]
)
encoded += bytes([_b32tab[c1 >> 11], # bits 1 - 5
_b32tab[(c1 >> 6) & 0x1f], # bits 6 - 10
_b32tab[(c1 >> 1) & 0x1f], # bits 11 - 15
_b32tab[c2 >> 12], # bits 16 - 20 (1 - 5)
_b32tab[(c2 >> 7) & 0x1f], # bits 21 - 25 (6 - 10)
_b32tab[(c2 >> 2) & 0x1f], # bits 26 - 30 (11 - 15)
_b32tab[c3 >> 5], # bits 31 - 35 (1 - 5)
_b32tab[c3 & 0x1f], # bits 36 - 40 (1 - 5)
])
# Adjust for any leftover partial quanta
if leftover == 1:
encoded = encoded[:-6] + b"======"
encoded = encoded[:-6] + b'======'
elif leftover == 2:
encoded = encoded[:-4] + b"===="
encoded = encoded[:-4] + b'===='
elif leftover == 3:
encoded = encoded[:-3] + b"==="
encoded = encoded[:-3] + b'==='
elif leftover == 4:
encoded = encoded[:-1] + b"="
encoded = encoded[:-1] + b'='
return bytes(encoded)
@ -278,20 +222,20 @@ def b32decode(s, casefold=False, map01=None):
s = _bytes_from_decode_data(s)
quanta, leftover = divmod(len(s), 8)
if leftover:
raise binascii.Error("Incorrect padding")
raise binascii.Error('Incorrect padding')
# Handle section 2.4 zero and one mapping. The flag map01 will be either
# False, or the character to map the digit 1 (one) to. It should be
# either L (el) or I (eye).
if map01 is not None:
map01 = _bytes_from_decode_data(map01)
assert len(map01) == 1, repr(map01)
s = _translate(s, _maketrans(b"01", b"O" + map01))
s = s.translate(bytes.maketrans(b'01', b'O' + map01))
if casefold:
s = s.upper()
# Strip off pad characters from the right. We need to count the pad
# characters because this will tell us how many null bytes to remove from
# the end of the decoded string.
padchars = s.find(b"=")
padchars = s.find(b'=')
if padchars > 0:
padchars = len(s) - padchars
s = s[:-padchars]
@ -305,17 +249,17 @@ def b32decode(s, casefold=False, map01=None):
for c in s:
val = _b32rev.get(c)
if val is None:
raise binascii.Error("Non-base32 digit found")
raise binascii.Error('Non-base32 digit found')
acc += _b32rev[c] << shift
shift -= 5
if shift < 0:
parts.append(binascii.unhexlify(bytes("%010x" % acc, "ascii")))
parts.append(binascii.unhexlify(bytes('%010x' % acc, "ascii")))
acc = 0
shift = 35
# Process the last, partial quanta
last = binascii.unhexlify(bytes("%010x" % acc, "ascii"))
last = binascii.unhexlify(bytes('%010x' % acc, "ascii"))
if padchars == 0:
last = b"" # No characters
last = b'' # No characters
elif padchars == 1:
last = last[:-1]
elif padchars == 3:
@ -325,9 +269,10 @@ def b32decode(s, casefold=False, map01=None):
elif padchars == 6:
last = last[:-4]
else:
raise binascii.Error("Incorrect padding")
raise binascii.Error('Incorrect padding')
parts.append(last)
return b"".join(parts)
return b''.join(parts)
# RFC 3548, Base 16 Alphabet specifies uppercase, but hexlify() returns
@ -357,18 +302,18 @@ def b16decode(s, casefold=False):
s = _bytes_from_decode_data(s)
if casefold:
s = s.upper()
if re.search(b"[^0-9A-F]", s):
raise binascii.Error("Non-base16 digit found")
if re.search(b'[^0-9A-F]', s):
raise binascii.Error('Non-base16 digit found')
return binascii.unhexlify(s)
# Legacy interface. This code could be cleaned up since I don't believe
# binascii has any line length limitations. It just doesn't seem worth it
# though. The files should be opened in binary mode.
MAXLINESIZE = 76 # Excluding the CRLF
MAXBINSIZE = (MAXLINESIZE // 4) * 3
MAXLINESIZE = 76 # Excluding the CRLF
MAXBINSIZE = (MAXLINESIZE//4)*3
def encode(input, output):
"""Encode a file; input and output are binary files."""
@ -377,7 +322,7 @@ def encode(input, output):
if not s:
break
while len(s) < MAXBINSIZE:
ns = input.read(MAXBINSIZE - len(s))
ns = input.read(MAXBINSIZE-len(s))
if not ns:
break
s += ns
@ -406,12 +351,11 @@ def encodebytes(s):
pieces.append(binascii.b2a_base64(chunk))
return b"".join(pieces)
def encodestring(s):
"""Legacy alias of encodebytes()."""
import warnings
warnings.warn("encodestring() is a deprecated alias, use encodebytes()", DeprecationWarning, 2)
warnings.warn("encodestring() is a deprecated alias, use encodebytes()",
DeprecationWarning, 2)
return encodebytes(s)
@ -421,12 +365,11 @@ def decodebytes(s):
raise TypeError("expected bytes, not %s" % s.__class__.__name__)
return binascii.a2b_base64(s)
def decodestring(s):
"""Legacy alias of decodebytes()."""
import warnings
warnings.warn("decodestring() is a deprecated alias, use decodebytes()", DeprecationWarning, 2)
warnings.warn("decodestring() is a deprecated alias, use decodebytes()",
DeprecationWarning, 2)
return decodebytes(s)
@ -434,33 +377,24 @@ def decodestring(s):
def main():
"""Small main program"""
import sys, getopt
try:
opts, args = getopt.getopt(sys.argv[1:], "deut")
opts, args = getopt.getopt(sys.argv[1:], 'deut')
except getopt.error as msg:
sys.stdout = sys.stderr
print(msg)
print(
"""usage: %s [-d|-e|-u|-t] [file|-]
print("""usage: %s [-d|-e|-u|-t] [file|-]
-d, -u: decode
-e: encode (default)
-t: encode and decode string 'Aladdin:open sesame'"""
% sys.argv[0]
)
-t: encode and decode string 'Aladdin:open sesame'"""%sys.argv[0])
sys.exit(2)
func = encode
for o, a in opts:
if o == "-e":
func = encode
if o == "-d":
func = decode
if o == "-u":
func = decode
if o == "-t":
test()
return
if args and args[0] != "-":
with open(args[0], "rb") as f:
if o == '-e': func = encode
if o == '-d': func = decode
if o == '-u': func = decode
if o == '-t': test(); return
if args and args[0] != '-':
with open(args[0], 'rb') as f:
func(f, sys.stdout.buffer)
else:
func(sys.stdin.buffer, sys.stdout.buffer)
@ -476,5 +410,5 @@ def test():
assert s0 == s2
if __name__ == "__main__":
if __name__ == '__main__':
main()

Wyświetl plik

@ -0,0 +1,4 @@
srctype=cpython
type=module
version=3.3.3-2
depends = struct

19
base64/setup.py 100644
Wyświetl plik

@ -0,0 +1,19 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-base64',
version='3.3.3-2',
description='CPython base64 module ported to MicroPython',
long_description='This is a module ported from CPython standard library to be compatible with\nMicroPython interpreter. Usually, this means applying small patches for\nfeatures not supported (yet, or at all) in MicroPython. Sometimes, heavier\nchanges are required. Note that CPython modules are written with availability\nof vast resources in mind, and may not work for MicroPython ports with\nlimited heap. If you are affected by such a case, please help reimplement\nthe module from scratch.',
url='https://github.com/micropython/micropython/issues/405',
author='CPython Developers',
author_email='python-dev@python.org',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='Python',
py_modules=['base64'],
install_requires=['micropython-struct'])

Wyświetl plik

@ -1,22 +1,22 @@
import base64
b = base64.b64encode(b"zlutoucky kun upel dabelske ody")
b = base64.b64encode(b'zlutoucky kun upel dabelske ody')
print(b)
if b != b"emx1dG91Y2t5IGt1biB1cGVsIGRhYmVsc2tlIG9keQ==":
if b != b'emx1dG91Y2t5IGt1biB1cGVsIGRhYmVsc2tlIG9keQ==':
raise Exception("Error")
d = base64.b64decode(b)
print(d)
if d != b"zlutoucky kun upel dabelske ody":
if d != b'zlutoucky kun upel dabelske ody':
raise Exception("Error")
base64.test()
binary = b"\x99\x10\xaa"
binary = b'\x99\x10\xaa'
b = base64.b64encode(binary)
if b != b"mRCq":
if b != b'mRCq':
raise Exception("Error")
d = base64.b64decode(b)
@ -24,13 +24,13 @@ print(d)
if d != binary:
raise Exception("Error")
d = base64.b32encode(b"zlutoucky kun upel dabelske ody")
if d != b"PJWHK5DPOVRWW6JANN2W4IDVOBSWYIDEMFRGK3DTNNSSA33EPE======":
d = base64.b32encode(b'zlutoucky kun upel dabelske ody')
if d != b'PJWHK5DPOVRWW6JANN2W4IDVOBSWYIDEMFRGK3DTNNSSA33EPE======':
raise Exception("Error")
print(d)
b = base64.b32decode(d)
if b != b"zlutoucky kun upel dabelske ody":
if b != b'zlutoucky kun upel dabelske ody':
raise Exception("Error")
print("OK")

Wyświetl plik

@ -0,0 +1,113 @@
from ubinascii import *
if not "unhexlify" in globals():
def unhexlify(data):
if len(data) % 2 != 0:
raise ValueError("Odd-length string")
return bytes([ int(data[i:i+2], 16) for i in range(0, len(data), 2) ])
b2a_hex = hexlify
a2b_hex = unhexlify
# ____________________________________________________________
PAD = '='
table_a2b_base64 = [
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1, # Note PAD->-1 here
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
]
def _transform(n):
if n == -1:
return '\xff'
else:
return chr(n)
table_a2b_base64 = ''.join(map(_transform, table_a2b_base64))
assert len(table_a2b_base64) == 256
def a2b_base64(ascii):
"Decode a line of base64 data."
res = []
quad_pos = 0
leftchar = 0
leftbits = 0
last_char_was_a_pad = False
for c in ascii:
c = chr(c)
if c == PAD:
if quad_pos > 2 or (quad_pos == 2 and last_char_was_a_pad):
break # stop on 'xxx=' or on 'xx=='
last_char_was_a_pad = True
else:
n = ord(table_a2b_base64[ord(c)])
if n == 0xff:
continue # ignore strange characters
#
# Shift it in on the low end, and see if there's
# a byte ready for output.
quad_pos = (quad_pos + 1) & 3
leftchar = (leftchar << 6) | n
leftbits += 6
#
if leftbits >= 8:
leftbits -= 8
res.append((leftchar >> leftbits).to_bytes(1))
leftchar &= ((1 << leftbits) - 1)
#
last_char_was_a_pad = False
else:
if leftbits != 0:
raise Exception("Incorrect padding")
return b''.join(res)
# ____________________________________________________________
table_b2a_base64 = (
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
def b2a_base64(bin):
"Base64-code line of data."
newlength = (len(bin) + 2) // 3
newlength = newlength * 4 + 1
res = []
leftchar = 0
leftbits = 0
for c in bin:
# Shift into our buffer, and output any 6bits ready
leftchar = (leftchar << 8) | c
leftbits += 8
res.append(table_b2a_base64[(leftchar >> (leftbits-6)) & 0x3f])
leftbits -= 6
if leftbits >= 6:
res.append(table_b2a_base64[(leftchar >> (leftbits-6)) & 0x3f])
leftbits -= 6
#
if leftbits == 2:
res.append(table_b2a_base64[(leftchar & 3) << 4])
res.append(PAD)
res.append(PAD)
elif leftbits == 4:
res.append(table_b2a_base64[(leftchar & 0xf) << 2])
res.append(PAD)
res.append('\n')
return ''.join(res).encode('ascii')

Wyświetl plik

@ -0,0 +1,3 @@
srctype=pypy
type=module
version=2.4.0-3

18
binascii/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-binascii',
version='2.4.0-3',
description='PyPy binascii module ported to MicroPython',
long_description='This is a module ported from PyPy standard library to be compatible with\nMicroPython interpreter. Usually, this means applying small patches for\nfeatures not supported (yet, or at all) in MicroPython. Sometimes, heavier\nchanges are required. Note that CPython modules are written with availability\nof vast resources in mind, and may not work for MicroPython ports with\nlimited heap. If you are affected by such a case, please help reimplement\nthe module from scratch.',
url='https://github.com/micropython/micropython/issues/405',
author='PyPy Developers',
author_email='pypy-dev@python.org',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['binascii'])

Wyświetl plik

@ -0,0 +1,21 @@
from binascii import hexlify, unhexlify
import utime
data = b'zlutoucky kun upel dabelske ody'
h = hexlify(data)
if h != b'7a6c75746f75636b79206b756e207570656c20646162656c736b65206f6479':
raise Exception("Error")
data2 = unhexlify(h)
if data2 != data:
raise Exception("Error")
start = utime.time()
for x in range(100000):
d = unhexlify(h)
print("100000 iterations in: " + str(utime.time() - start))
print("OK")

0
binhex/binhex.py 100644
Wyświetl plik

Wyświetl plik

@ -0,0 +1,3 @@
srctype=dummy
type=module
version=0.0.1

18
binhex/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-binhex',
version='0.0.1',
description='Dummy binhex module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['binhex'])

Wyświetl plik

@ -1,6 +1,5 @@
"""Bisection algorithms."""
def insort_right(a, x, lo=0, hi=None):
"""Insert item x in list a, and keep it sorted assuming a is sorted.
@ -11,20 +10,16 @@ def insort_right(a, x, lo=0, hi=None):
"""
if lo < 0:
raise ValueError("lo must be non-negative")
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo + hi) // 2
if x < a[mid]:
hi = mid
else:
lo = mid + 1
mid = (lo+hi)//2
if x < a[mid]: hi = mid
else: lo = mid+1
a.insert(lo, x)
insort = insort_right # backward compatibility
insort = insort_right # backward compatibility
def bisect_right(a, x, lo=0, hi=None):
"""Return the index where to insert item x in list a, assuming a is sorted.
@ -38,20 +33,16 @@ def bisect_right(a, x, lo=0, hi=None):
"""
if lo < 0:
raise ValueError("lo must be non-negative")
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo + hi) // 2
if x < a[mid]:
hi = mid
else:
lo = mid + 1
mid = (lo+hi)//2
if x < a[mid]: hi = mid
else: lo = mid+1
return lo
bisect = bisect_right # backward compatibility
bisect = bisect_right # backward compatibility
def insort_left(a, x, lo=0, hi=None):
"""Insert item x in list a, and keep it sorted assuming a is sorted.
@ -63,15 +54,13 @@ def insort_left(a, x, lo=0, hi=None):
"""
if lo < 0:
raise ValueError("lo must be non-negative")
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo + hi) // 2
if a[mid] < x:
lo = mid + 1
else:
hi = mid
mid = (lo+hi)//2
if a[mid] < x: lo = mid+1
else: hi = mid
a.insert(lo, x)
@ -87,18 +76,15 @@ def bisect_left(a, x, lo=0, hi=None):
"""
if lo < 0:
raise ValueError("lo must be non-negative")
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo + hi) // 2
if a[mid] < x:
lo = mid + 1
else:
hi = mid
mid = (lo+hi)//2
if a[mid] < x: lo = mid+1
else: hi = mid
return lo
# Overwrite above definitions with a fast C implementation
try:
from _bisect import *

23
bisect/setup.py 100644
Wyświetl plik

@ -0,0 +1,23 @@
import sys
# Remove current dir from sys.path, otherwise distutils will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
def desc_dummy(name):
return 'Dummy %s module to MicroPython' % name
def desc_cpython(name):
return 'CPython %s module ported to MicroPython' % name
NAME = 'bisect'
setup(name='micropython-' + NAME,
version='0.5',
description=desc_cpython(NAME),
url='https://github.com/micropython/micropython/issues/405',
author='CPython Developers',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='Python',
py_modules=[NAME])

Wyświetl plik

Wyświetl plik

@ -0,0 +1,3 @@
srctype=dummy
type=module
version=0.0.0

18
calendar/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-calendar',
version='0.0.0',
description='Dummy calendar module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['calendar'])

Wyświetl plik

@ -25,6 +25,9 @@ written in Python.
# responsible for its maintenance.
#
__version__ = "2.6"
# Imports
# =======
@ -38,29 +41,17 @@ import html
import locale
import tempfile
__all__ = [
"MiniFieldStorage",
"FieldStorage",
"parse",
"parse_qs",
"parse_qsl",
"parse_multipart",
"parse_header",
"print_exception",
"print_environ",
"print_form",
"print_directory",
"print_arguments",
"print_environ_usage",
"escape",
]
__all__ = ["MiniFieldStorage", "FieldStorage",
"parse", "parse_qs", "parse_qsl", "parse_multipart",
"parse_header", "print_exception", "print_environ",
"print_form", "print_directory", "print_arguments",
"print_environ_usage", "escape"]
# Logging support
# ===============
logfile = "" # Filename to log to, if not empty
logfp = None # File object to log to, if not None
logfile = "" # Filename to log to, if not empty
logfp = None # File object to log to, if not None
def initlog(*allargs):
"""Write a log message, if there is a log file.
@ -97,28 +88,24 @@ def initlog(*allargs):
log = dolog
log(*allargs)
def dolog(fmt, *args):
"""Write a log message to the log file. See initlog() for docs."""
logfp.write(fmt % args + "\n")
logfp.write(fmt%args + "\n")
def nolog(*allargs):
"""Dummy function, assigned to log when logging is disabled."""
pass
def closelog():
"""Close the log file."""
global log, logfile, logfp
logfile = ""
logfile = ''
if logfp:
logfp.close()
logfp = None
log = initlog
log = initlog # The current logging function
log = initlog # The current logging function
# Parsing functions
@ -128,90 +115,87 @@ log = initlog # The current logging function
# 0 ==> unlimited input
maxlen = 0
def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0):
"""Parse a query in the environment or from a file (default stdin)
Arguments, all optional:
Arguments, all optional:
fp : file pointer; default: sys.stdin.buffer
fp : file pointer; default: sys.stdin.buffer
environ : environment dictionary; default: os.environ
environ : environment dictionary; default: os.environ
keep_blank_values: flag indicating whether blank values in
percent-encoded forms should be treated as blank strings.
A true value indicates that blanks should be retained as
blank strings. The default false value indicates that
blank values are to be ignored and treated as if they were
not included.
keep_blank_values: flag indicating whether blank values in
percent-encoded forms should be treated as blank strings.
A true value indicates that blanks should be retained as
blank strings. The default false value indicates that
blank values are to be ignored and treated as if they were
not included.
strict_parsing: flag indicating what to do with parsing errors.
If false (the default), errors are silently ignored.
If true, errors raise a ValueError exception.
strict_parsing: flag indicating what to do with parsing errors.
If false (the default), errors are silently ignored.
If true, errors raise a ValueError exception.
"""
if fp is None:
fp = sys.stdin
# field keys and values (except for files) are returned as strings
# an encoding is required to decode the bytes read from self.fp
if hasattr(fp, "encoding"):
if hasattr(fp,'encoding'):
encoding = fp.encoding
else:
encoding = "latin-1"
encoding = 'latin-1'
# fp.read() must return bytes
if isinstance(fp, TextIOWrapper):
fp = fp.buffer
if not "REQUEST_METHOD" in environ:
environ["REQUEST_METHOD"] = "GET" # For testing stand-alone
if environ["REQUEST_METHOD"] == "POST":
ctype, pdict = parse_header(environ["CONTENT_TYPE"])
if ctype == "multipart/form-data":
if not 'REQUEST_METHOD' in environ:
environ['REQUEST_METHOD'] = 'GET' # For testing stand-alone
if environ['REQUEST_METHOD'] == 'POST':
ctype, pdict = parse_header(environ['CONTENT_TYPE'])
if ctype == 'multipart/form-data':
return parse_multipart(fp, pdict)
elif ctype == "application/x-www-form-urlencoded":
clength = int(environ["CONTENT_LENGTH"])
elif ctype == 'application/x-www-form-urlencoded':
clength = int(environ['CONTENT_LENGTH'])
if maxlen and clength > maxlen:
raise ValueError("Maximum content length exceeded")
raise ValueError('Maximum content length exceeded')
qs = fp.read(clength).decode(encoding)
else:
qs = "" # Unknown content-type
if "QUERY_STRING" in environ:
if qs:
qs = qs + "&"
qs = qs + environ["QUERY_STRING"]
qs = '' # Unknown content-type
if 'QUERY_STRING' in environ:
if qs: qs = qs + '&'
qs = qs + environ['QUERY_STRING']
elif sys.argv[1:]:
if qs:
qs = qs + "&"
if qs: qs = qs + '&'
qs = qs + sys.argv[1]
environ["QUERY_STRING"] = qs # XXX Shouldn't, really
elif "QUERY_STRING" in environ:
qs = environ["QUERY_STRING"]
environ['QUERY_STRING'] = qs # XXX Shouldn't, really
elif 'QUERY_STRING' in environ:
qs = environ['QUERY_STRING']
else:
if sys.argv[1:]:
qs = sys.argv[1]
else:
qs = ""
environ["QUERY_STRING"] = qs # XXX Shouldn't, really
return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing, encoding=encoding)
environ['QUERY_STRING'] = qs # XXX Shouldn't, really
return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing,
encoding=encoding)
# parse query string function called from urlparse,
# this is done in order to maintain backward compatiblity.
def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
"""Parse a query given as a string argument."""
warn("cgi.parse_qs is deprecated, use urllib.parse.parse_qs instead", DeprecationWarning, 2)
warn("cgi.parse_qs is deprecated, use urllib.parse.parse_qs instead",
DeprecationWarning, 2)
return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing)
def parse_qsl(qs, keep_blank_values=0, strict_parsing=0):
"""Parse a query given as a string argument."""
warn("cgi.parse_qsl is deprecated, use urllib.parse.parse_qsl instead", DeprecationWarning, 2)
warn("cgi.parse_qsl is deprecated, use urllib.parse.parse_qsl instead",
DeprecationWarning, 2)
return urllib.parse.parse_qsl(qs, keep_blank_values, strict_parsing)
def parse_multipart(fp, pdict):
"""Parse multipart input.
@ -240,10 +224,11 @@ def parse_multipart(fp, pdict):
import http.client
boundary = b""
if "boundary" in pdict:
boundary = pdict["boundary"]
if 'boundary' in pdict:
boundary = pdict['boundary']
if not valid_boundary(boundary):
raise ValueError("Invalid boundary in multipart form: %r" % (boundary,))
raise ValueError('Invalid boundary in multipart form: %r'
% (boundary,))
nextpart = b"--" + boundary
lastpart = b"--" + boundary + b"--"
@ -256,7 +241,7 @@ def parse_multipart(fp, pdict):
if terminator:
# At start of next part. Read headers first.
headers = http.client.parse_headers(fp)
clength = headers.get("content-length")
clength = headers.get('content-length')
if clength:
try:
bytes = int(clength)
@ -264,7 +249,7 @@ def parse_multipart(fp, pdict):
pass
if bytes > 0:
if maxlen and bytes > maxlen:
raise ValueError("Maximum content length exceeded")
raise ValueError('Maximum content length exceeded')
data = fp.read(bytes)
else:
data = b""
@ -273,7 +258,7 @@ def parse_multipart(fp, pdict):
while 1:
line = fp.readline()
if not line:
terminator = lastpart # End outer loop
terminator = lastpart # End outer loop
break
if line.startswith(b"--"):
terminator = line.rstrip()
@ -293,14 +278,14 @@ def parse_multipart(fp, pdict):
line = line[:-1]
lines[-1] = line
data = b"".join(lines)
line = headers["content-disposition"]
line = headers['content-disposition']
if not line:
continue
key, params = parse_header(line)
if key != "form-data":
if key != 'form-data':
continue
if "name" in params:
name = params["name"]
if 'name' in params:
name = params['name']
else:
continue
if name in partdict:
@ -312,35 +297,34 @@ def parse_multipart(fp, pdict):
def _parseparam(s):
while s[:1] == ";":
while s[:1] == ';':
s = s[1:]
end = s.find(";")
end = s.find(';')
while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2:
end = s.find(";", end + 1)
end = s.find(';', end + 1)
if end < 0:
end = len(s)
f = s[:end]
yield f.strip()
s = s[end:]
def parse_header(line):
"""Parse a Content-type like header.
Return the main content-type and a dictionary of options.
"""
parts = _parseparam(";" + line)
parts = _parseparam(';' + line)
key = parts.__next__()
pdict = {}
for p in parts:
i = p.find("=")
i = p.find('=')
if i >= 0:
name = p[:i].strip().lower()
value = p[i + 1 :].strip()
value = p[i+1:].strip()
if len(value) >= 2 and value[0] == value[-1] == '"':
value = value[1:-1]
value = value.replace("\\\\", "\\").replace('\\"', '"')
value = value.replace('\\\\', '\\').replace('\\"', '"')
pdict[name] = value
return key, pdict
@ -348,7 +332,6 @@ def parse_header(line):
# Classes for field storage
# =========================
class MiniFieldStorage:
"""Like FieldStorage, for use when no file uploads are possible."""
@ -417,19 +400,9 @@ class FieldStorage:
directory and unlinking them as soon as they have been opened.
"""
def __init__(
self,
fp=None,
headers=None,
outerboundary=b"",
environ=os.environ,
keep_blank_values=0,
strict_parsing=0,
limit=None,
encoding="utf-8",
errors="replace",
):
def __init__(self, fp=None, headers=None, outerboundary=b'',
environ=os.environ, keep_blank_values=0, strict_parsing=0,
limit=None, encoding='utf-8', errors='replace'):
"""Constructor. Read multipart/* until last part.
Arguments, all optional:
@ -470,34 +443,35 @@ class FieldStorage:
header)
"""
method = "GET"
method = 'GET'
self.keep_blank_values = keep_blank_values
self.strict_parsing = strict_parsing
if "REQUEST_METHOD" in environ:
method = environ["REQUEST_METHOD"].upper()
if 'REQUEST_METHOD' in environ:
method = environ['REQUEST_METHOD'].upper()
self.qs_on_post = None
if method == "GET" or method == "HEAD":
if "QUERY_STRING" in environ:
qs = environ["QUERY_STRING"]
if method == 'GET' or method == 'HEAD':
if 'QUERY_STRING' in environ:
qs = environ['QUERY_STRING']
elif sys.argv[1:]:
qs = sys.argv[1]
else:
qs = ""
qs = qs.encode(locale.getpreferredencoding(), "surrogateescape")
qs = qs.encode(locale.getpreferredencoding(), 'surrogateescape')
fp = BytesIO(qs)
if headers is None:
headers = {"content-type": "application/x-www-form-urlencoded"}
headers = {'content-type':
"application/x-www-form-urlencoded"}
if headers is None:
headers = {}
if method == "POST":
if method == 'POST':
# Set default content-type for POST to what's traditional
headers["content-type"] = "application/x-www-form-urlencoded"
if "CONTENT_TYPE" in environ:
headers["content-type"] = environ["CONTENT_TYPE"]
if "QUERY_STRING" in environ:
self.qs_on_post = environ["QUERY_STRING"]
if "CONTENT_LENGTH" in environ:
headers["content-length"] = environ["CONTENT_LENGTH"]
headers['content-type'] = "application/x-www-form-urlencoded"
if 'CONTENT_TYPE' in environ:
headers['content-type'] = environ['CONTENT_TYPE']
if 'QUERY_STRING' in environ:
self.qs_on_post = environ['QUERY_STRING']
if 'CONTENT_LENGTH' in environ:
headers['content-length'] = environ['CONTENT_LENGTH']
if fp is None:
self.fp = sys.stdin.buffer
# self.fp.read() must return bytes
@ -511,7 +485,8 @@ class FieldStorage:
self.headers = headers
if not isinstance(outerboundary, bytes):
raise TypeError("outerboundary must be bytes, not %s" % type(outerboundary).__name__)
raise TypeError('outerboundary must be bytes, not %s'
% type(outerboundary).__name__)
self.outerboundary = outerboundary
self.bytes_read = 0
@ -519,16 +494,16 @@ class FieldStorage:
# Process content-disposition header
cdisp, pdict = "", {}
if "content-disposition" in self.headers:
cdisp, pdict = parse_header(self.headers["content-disposition"])
if 'content-disposition' in self.headers:
cdisp, pdict = parse_header(self.headers['content-disposition'])
self.disposition = cdisp
self.disposition_options = pdict
self.name = None
if "name" in pdict:
self.name = pdict["name"]
if 'name' in pdict:
self.name = pdict['name']
self.filename = None
if "filename" in pdict:
self.filename = pdict["filename"]
if 'filename' in pdict:
self.filename = pdict['filename']
self._binary_file = self.filename is not None
# Process content-type header
@ -543,49 +518,50 @@ class FieldStorage:
#
# See below for what we do if there does exist a content-type header,
# but it happens to be something we don't understand.
if "content-type" in self.headers:
ctype, pdict = parse_header(self.headers["content-type"])
elif self.outerboundary or method != "POST":
if 'content-type' in self.headers:
ctype, pdict = parse_header(self.headers['content-type'])
elif self.outerboundary or method != 'POST':
ctype, pdict = "text/plain", {}
else:
ctype, pdict = "application/x-www-form-urlencoded", {}
ctype, pdict = 'application/x-www-form-urlencoded', {}
self.type = ctype
self.type_options = pdict
if "boundary" in pdict:
self.innerboundary = pdict["boundary"].encode(self.encoding)
if 'boundary' in pdict:
self.innerboundary = pdict['boundary'].encode(self.encoding)
else:
self.innerboundary = b""
clen = -1
if "content-length" in self.headers:
if 'content-length' in self.headers:
try:
clen = int(self.headers["content-length"])
clen = int(self.headers['content-length'])
except ValueError:
pass
if maxlen and clen > maxlen:
raise ValueError("Maximum content length exceeded")
raise ValueError('Maximum content length exceeded')
self.length = clen
if self.limit is None and clen:
self.limit = clen
self.list = self.file = None
self.done = 0
if ctype == "application/x-www-form-urlencoded":
if ctype == 'application/x-www-form-urlencoded':
self.read_urlencoded()
elif ctype[:10] == "multipart/":
elif ctype[:10] == 'multipart/':
self.read_multi(environ, keep_blank_values, strict_parsing)
else:
self.read_single()
def __repr__(self):
"""Return a printable representation."""
return "FieldStorage(%r, %r, %r)" % (self.name, self.filename, self.value)
return "FieldStorage(%r, %r, %r)" % (
self.name, self.filename, self.value)
def __iter__(self):
return iter(self.keys())
def __getattr__(self, name):
if name != "value":
if name != 'value':
raise AttributeError(name)
if self.file:
self.file.seek(0)
@ -603,8 +579,7 @@ class FieldStorage:
raise TypeError("not indexable")
found = []
for item in self.list:
if item.name == key:
found.append(item)
if item.name == key: found.append(item)
if not found:
raise KeyError(key)
if len(found) == 1:
@ -624,7 +599,7 @@ class FieldStorage:
return default
def getfirst(self, key, default=None):
"""Return the first value received."""
""" Return the first value received."""
if key in self:
value = self[key]
if isinstance(value, list):
@ -635,7 +610,7 @@ class FieldStorage:
return default
def getlist(self, key):
"""Return list of received values."""
""" Return list of received values."""
if key in self:
value = self[key]
if isinstance(value, list):
@ -668,18 +643,15 @@ class FieldStorage:
"""Internal: read data in query string format."""
qs = self.fp.read(self.length)
if not isinstance(qs, bytes):
raise ValueError("%s should return bytes, got %s" % (self.fp, type(qs).__name__))
raise ValueError("%s should return bytes, got %s" \
% (self.fp, type(qs).__name__))
qs = qs.decode(self.encoding, self.errors)
if self.qs_on_post:
qs += "&" + self.qs_on_post
qs += '&' + self.qs_on_post
self.list = []
query = urllib.parse.parse_qsl(
qs,
self.keep_blank_values,
self.strict_parsing,
encoding=self.encoding,
errors=self.errors,
)
qs, self.keep_blank_values, self.strict_parsing,
encoding=self.encoding, errors=self.errors)
for key, value in query:
self.list.append(MiniFieldStorage(key, value))
self.skip_lines()
@ -690,26 +662,21 @@ class FieldStorage:
"""Internal: read a part that is itself multipart."""
ib = self.innerboundary
if not valid_boundary(ib):
raise ValueError("Invalid boundary in multipart form: %r" % (ib,))
raise ValueError('Invalid boundary in multipart form: %r' % (ib,))
self.list = []
if self.qs_on_post:
query = urllib.parse.parse_qsl(
self.qs_on_post,
self.keep_blank_values,
self.strict_parsing,
encoding=self.encoding,
errors=self.errors,
)
self.qs_on_post, self.keep_blank_values, self.strict_parsing,
encoding=self.encoding, errors=self.errors)
for key, value in query:
self.list.append(MiniFieldStorage(key, value))
FieldStorageClass = None
klass = self.FieldStorageClass or self.__class__
first_line = self.fp.readline() # bytes
first_line = self.fp.readline() # bytes
if not isinstance(first_line, bytes):
raise ValueError(
"%s should return bytes, got %s" % (self.fp, type(first_line).__name__)
)
raise ValueError("%s should return bytes, got %s" \
% (self.fp, type(first_line).__name__))
self.bytes_read += len(first_line)
# first line holds boundary ; ignore it, or check that
# b"--" + ib == first_line.strip() ?
@ -727,17 +694,9 @@ class FieldStorage:
self.bytes_read += len(hdr_text)
parser.feed(hdr_text.decode(self.encoding, self.errors))
headers = parser.close()
part = klass(
self.fp,
headers,
ib,
environ,
keep_blank_values,
strict_parsing,
self.limit - self.bytes_read,
self.encoding,
self.errors,
)
part = klass(self.fp, headers, ib, environ, keep_blank_values,
strict_parsing,self.limit-self.bytes_read,
self.encoding, self.errors)
self.bytes_read += part.bytes_read
self.list.append(part)
if part.done or self.bytes_read >= self.length > 0:
@ -753,7 +712,7 @@ class FieldStorage:
self.read_lines()
self.file.seek(0)
bufsize = 8 * 1024 # I/O buffering size for copy to file
bufsize = 8*1024 # I/O buffering size for copy to file
def read_binary(self):
"""Internal: read binary data."""
@ -761,11 +720,10 @@ class FieldStorage:
todo = self.length
if todo >= 0:
while todo > 0:
data = self.fp.read(min(todo, self.bufsize)) # bytes
data = self.fp.read(min(todo, self.bufsize)) # bytes
if not isinstance(data, bytes):
raise ValueError(
"%s should return bytes, got %s" % (self.fp, type(data).__name__)
)
raise ValueError("%s should return bytes, got %s"
% (self.fp, type(data).__name__))
self.bytes_read += len(data)
if not data:
self.done = -1
@ -776,9 +734,9 @@ class FieldStorage:
def read_lines(self):
"""Internal: read lines until EOF or outerboundary."""
if self._binary_file:
self.file = self.__file = BytesIO() # store data as bytes for files
self.file = self.__file = BytesIO() # store data as bytes for files
else:
self.file = self.__file = StringIO() # as strings for other fields
self.file = self.__file = StringIO() # as strings for other fields
if self.outerboundary:
self.read_lines_to_outerboundary()
else:
@ -802,7 +760,7 @@ class FieldStorage:
def read_lines_to_eof(self):
"""Internal: read lines until EOF."""
while 1:
line = self.fp.readline(1 << 16) # bytes
line = self.fp.readline(1<<16) # bytes
self.bytes_read += len(line)
if not line:
self.done = -1
@ -822,7 +780,7 @@ class FieldStorage:
while 1:
if _read >= self.limit:
break
line = self.fp.readline(1 << 16) # bytes
line = self.fp.readline(1<<16) # bytes
self.bytes_read += len(line)
_read += len(line)
if not line:
@ -866,7 +824,7 @@ class FieldStorage:
last_boundary = next_boundary + b"--"
last_line_lfend = True
while True:
line = self.fp.readline(1 << 16)
line = self.fp.readline(1<<16)
self.bytes_read += len(line)
if not line:
self.done = -1
@ -878,7 +836,7 @@ class FieldStorage:
if strippedline == last_boundary:
self.done = 1
break
last_line_lfend = line.endswith(b"\n")
last_line_lfend = line.endswith(b'\n')
def make_file(self):
"""Overridable: return a readable & writable file.
@ -907,13 +865,13 @@ class FieldStorage:
if self._binary_file:
return tempfile.TemporaryFile("wb+")
else:
return tempfile.TemporaryFile("w+", encoding=self.encoding, newline="\n")
return tempfile.TemporaryFile("w+",
encoding=self.encoding, newline = '\n')
# Test/debug code
# ===============
def test(environ=os.environ):
"""Robust test CGI script, usable as main program.
@ -923,21 +881,18 @@ def test(environ=os.environ):
"""
print("Content-type: text/html")
print()
# sys.stderr = sys.stdout
#sys.stderr = sys.stdout
try:
form = FieldStorage() # Replace with other classes to test those
form = FieldStorage() # Replace with other classes to test those
print_directory()
print_arguments()
print_form(form)
print_environ(environ)
print_environ_usage()
def f():
exec("testing print_exception() -- <I>italics?</I>")
def g(f=f):
f()
print("<H3>What follows is a test, not an actual exception:</H3>")
g()
except:
@ -948,7 +903,7 @@ def test(environ=os.environ):
global maxlen
maxlen = 50
try:
form = FieldStorage() # Replace with other classes to test those
form = FieldStorage() # Replace with other classes to test those
print_directory()
print_arguments()
print_form(form)
@ -956,25 +911,20 @@ def test(environ=os.environ):
except:
print_exception()
def print_exception(type=None, value=None, tb=None, limit=None):
if type is None:
type, value, tb = sys.exc_info()
import traceback
print()
print("<H3>Traceback (most recent call last):</H3>")
list = traceback.format_tb(tb, limit) + traceback.format_exception_only(type, value)
print(
"<PRE>%s<B>%s</B></PRE>"
% (
html.escape("".join(list[:-1])),
html.escape(list[-1]),
)
)
list = traceback.format_tb(tb, limit) + \
traceback.format_exception_only(type, value)
print("<PRE>%s<B>%s</B></PRE>" % (
html.escape("".join(list[:-1])),
html.escape(list[-1]),
))
del tb
def print_environ(environ=os.environ):
"""Dump the shell environment as HTML."""
keys = sorted(environ.keys())
@ -986,7 +936,6 @@ def print_environ(environ=os.environ):
print("</DL>")
print()
def print_form(form):
"""Dump the contents of a form as HTML."""
keys = sorted(form.keys())
@ -996,14 +945,13 @@ def print_form(form):
print("<P>No form fields.")
print("<DL>")
for key in keys:
print("<DT>" + html.escape(key) + ":", end=" ")
print("<DT>" + html.escape(key) + ":", end=' ')
value = form[key]
print("<i>" + html.escape(repr(type(value))) + "</i>")
print("<DD>" + html.escape(repr(value)))
print("</DL>")
print()
def print_directory():
"""Dump the current directory as HTML."""
print()
@ -1016,7 +964,6 @@ def print_directory():
print(html.escape(pwd))
print()
def print_arguments():
print()
print("<H3>Command Line Arguments:</H3>")
@ -1024,11 +971,9 @@ def print_arguments():
print(sys.argv)
print()
def print_environ_usage():
"""Dump a list of environment variables used by CGI as HTML."""
print(
"""
print("""
<H3>These environment variables could have been set:</H3>
<UL>
<LI>AUTH_TYPE
@ -1067,18 +1012,17 @@ environment as well. Here are some common variable names:
<LI>HTTP_REFERER
<LI>HTTP_USER_AGENT
</UL>
"""
)
""")
# Utilities
# =========
def escape(s, quote=None):
"""Deprecated API."""
warn("cgi.escape is deprecated, use html.escape instead", DeprecationWarning, stacklevel=2)
s = s.replace("&", "&amp;") # Must be done first!
warn("cgi.escape is deprecated, use html.escape instead",
DeprecationWarning, stacklevel=2)
s = s.replace("&", "&amp;") # Must be done first!
s = s.replace("<", "&lt;")
s = s.replace(">", "&gt;")
if quote:
@ -1088,17 +1032,15 @@ def escape(s, quote=None):
def valid_boundary(s, _vb_pattern=None):
import re
if isinstance(s, bytes):
_vb_pattern = b"^[ -~]{0,200}[!-~]$"
else:
_vb_pattern = "^[ -~]{0,200}[!-~]$"
return re.match(_vb_pattern, s)
# Invoke mainline
# ===============
# Call test() when this file is run as a script (not imported as a module)
if __name__ == "__main__":
if __name__ == '__main__':
test()

3
cgi/metadata.txt 100644
Wyświetl plik

@ -0,0 +1,3 @@
srctype=cpython
type=module
version=3.3.3-1

18
cgi/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-cgi',
version='3.3.3-1',
description='CPython cgi module ported to MicroPython',
long_description='This is a module ported from CPython standard library to be compatible with\nMicroPython interpreter. Usually, this means applying small patches for\nfeatures not supported (yet, or at all) in MicroPython. Sometimes, heavier\nchanges are required. Note that CPython modules are written with availability\nof vast resources in mind, and may not work for MicroPython ports with\nlimited heap. If you are affected by such a case, please help reimplement\nthe module from scratch.',
url='https://github.com/micropython/micropython/issues/405',
author='CPython Developers',
author_email='python-dev@python.org',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='Python',
py_modules=['cgi'])

Wyświetl plik

@ -51,14 +51,14 @@ this means that that help by doc string feature doesn't work.
completions have also been stripped out.
"""
import sys
#import string, sys
import sys # MiroPython doesn't yet have a string module
__all__ = ["Cmd"]
PROMPT = "(Cmd) "
# This is equivalent to string.ascii_letters + string.digits + '_'
IDENTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"
PROMPT = '(Cmd) '
#IDENTCHARS = string.ascii_letters + string.digits + '_'
IDENTCHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'
class Cmd:
"""A simple framework for writing line-oriented command interpreters.
@ -72,11 +72,10 @@ class Cmd:
in order to inherit Cmd's methods and encapsulate action methods.
"""
prompt = PROMPT
identchars = IDENTCHARS
ruler = "="
lastcmd = ""
ruler = '='
lastcmd = ''
intro = None
doc_leader = ""
doc_header = "Documented commands (type help <topic>):"
@ -115,7 +114,7 @@ class Cmd:
if intro is not None:
self.intro = intro
if self.intro:
self.stdout.write(str(self.intro) + "\n")
self.stdout.write(str(self.intro)+"\n")
stop = None
while not stop:
if self.cmdqueue:
@ -125,15 +124,15 @@ class Cmd:
try:
line = input(self.prompt)
except EOFError:
line = "EOF"
line = 'EOF'
else:
self.stdout.write(self.prompt)
self.stdout.flush()
line = self.stdin.readline()
if not len(line):
line = "EOF"
line = 'EOF'
else:
line = line.rstrip("\r\n")
line = line.rstrip('\r\n')
line = self.precmd(line)
stop = self.onecmd(line)
stop = self.postcmd(stop, line)
@ -171,16 +170,15 @@ class Cmd:
line = line.strip()
if not line:
return None, None, line
elif line[0] == "?":
line = "help " + line[1:]
elif line[0] == "!":
if hasattr(self, "do_shell"):
line = "shell " + line[1:]
elif line[0] == '?':
line = 'help ' + line[1:]
elif line[0] == '!':
if hasattr(self, 'do_shell'):
line = 'shell ' + line[1:]
else:
return None, None, line
i, n = 0, len(line)
while i < n and line[i] in self.identchars:
i = i + 1
while i < n and line[i] in self.identchars: i = i+1
cmd, arg = line[:i], line[i:].strip()
return cmd, arg, line
@ -200,13 +198,13 @@ class Cmd:
if cmd is None:
return self.default(line)
self.lastcmd = line
if line == "EOF":
self.lastcmd = ""
if cmd == "":
if line == 'EOF' :
self.lastcmd = ''
if cmd == '':
return self.default(line)
else:
try:
func = getattr(self, "do_" + cmd)
func = getattr(self, 'do_' + cmd)
except AttributeError:
return self.default(line)
return func(arg)
@ -228,7 +226,7 @@ class Cmd:
returns.
"""
self.stdout.write("*** Unknown syntax: %s\n" % line)
self.stdout.write('*** Unknown syntax: %s\n'%line)
def get_names(self):
# This method used to pull in base class attributes
@ -240,9 +238,9 @@ class Cmd:
if arg:
# XXX check arg syntax
try:
func = getattr(self, "help_" + arg)
func = getattr(self, 'help_' + arg)
except AttributeError:
self.stdout.write("%s\n" % str(self.nohelp % (arg,)))
self.stdout.write("%s\n"%str(self.nohelp % (arg,)))
return
func()
else:
@ -251,33 +249,33 @@ class Cmd:
cmds_undoc = []
help = {}
for name in names:
if name[:5] == "help_":
help[name[5:]] = 1
if name[:5] == 'help_':
help[name[5:]]=1
names.sort()
# There can be duplicates if routines overridden
prevname = ""
prevname = ''
for name in names:
if name[:3] == "do_":
if name[:3] == 'do_':
if name == prevname:
continue
prevname = name
cmd = name[3:]
cmd=name[3:]
if cmd in help:
cmds_doc.append(cmd)
del help[cmd]
else:
cmds_undoc.append(cmd)
self.stdout.write("%s\n" % str(self.doc_leader))
self.print_topics(self.doc_header, cmds_doc, 15, 80)
self.print_topics(self.misc_header, list(help.keys()), 15, 80)
self.print_topics(self.undoc_header, cmds_undoc, 15, 80)
self.stdout.write("%s\n"%str(self.doc_leader))
self.print_topics(self.doc_header, cmds_doc, 15,80)
self.print_topics(self.misc_header, list(help.keys()),15,80)
self.print_topics(self.undoc_header, cmds_undoc, 15,80)
def print_topics(self, header, cmds, cmdlen, maxcol):
if cmds:
self.stdout.write("%s\n" % str(header))
self.stdout.write("%s\n"%str(header))
if self.ruler:
self.stdout.write("%s\n" % str(self.ruler * len(header)))
self.columnize(cmds, maxcol - 1)
self.stdout.write("%s\n"%str(self.ruler * len(header)))
self.columnize(cmds, maxcol-1)
self.stdout.write("\n")
def columnize(self, list, displaywidth=80):
@ -290,22 +288,24 @@ class Cmd:
self.stdout.write("<empty>\n")
return
nonstrings = [i for i in range(len(list)) if not isinstance(list[i], str)]
nonstrings = [i for i in range(len(list))
if not isinstance(list[i], str)]
if nonstrings:
raise TypeError("list[i] not a string for i in %s" % ", ".join(map(str, nonstrings)))
raise TypeError("list[i] not a string for i in %s"
% ", ".join(map(str, nonstrings)))
size = len(list)
if size == 1:
self.stdout.write("%s\n" % str(list[0]))
self.stdout.write('%s\n'%str(list[0]))
return
# Try every row count from 1 upwards
for nrows in range(1, len(list)):
ncols = (size + nrows - 1) // nrows
ncols = (size+nrows-1) // nrows
colwidths = []
totwidth = -2
for col in range(ncols):
colwidth = 0
for row in range(nrows):
i = row + nrows * col
i = row + nrows*col
if i >= size:
break
x = list[i]
@ -323,7 +323,7 @@ class Cmd:
for row in range(nrows):
texts = []
for col in range(ncols):
i = row + nrows * col
i = row + nrows*col
if i >= size:
x = ""
else:
@ -332,6 +332,6 @@ class Cmd:
while texts and not texts[-1]:
del texts[-1]
for col in range(len(texts)):
# texts[col] = texts[col].ljust(colwidths[col])
texts[col] = "%-*s" % (colwidths[col], texts[col])
self.stdout.write("%s\n" % str(" ".join(texts)))
#texts[col] = texts[col].ljust(colwidths[col])
texts[col] = '%-*s' % (colwidths[col], texts[col])
self.stdout.write("%s\n"%str(" ".join(texts)))

3
cmd/metadata.txt 100644
Wyświetl plik

@ -0,0 +1,3 @@
srctype = cpython
type = module
version = 3.4.0-1

18
cmd/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-cmd',
version='3.4.0-1',
description='CPython cmd module ported to MicroPython',
long_description='This is a module ported from CPython standard library to be compatible with\nMicroPython interpreter. Usually, this means applying small patches for\nfeatures not supported (yet, or at all) in MicroPython. Sometimes, heavier\nchanges are required. Note that CPython modules are written with availability\nof vast resources in mind, and may not work for MicroPython ports with\nlimited heap. If you are affected by such a case, please help reimplement\nthe module from scratch.',
url='https://github.com/micropython/micropython/issues/405',
author='CPython Developers',
author_email='python-dev@python.org',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='Python',
py_modules=['cmd'])

Wyświetl plik

@ -1,4 +1,5 @@
class defaultdict:
@staticmethod
def __new__(cls, default_factory=None, **kwargs):
# Some code (e.g. urllib.urlparse) expects that basic defaultdict
@ -26,9 +27,6 @@ class defaultdict:
def __delitem__(self, key):
del self.d[key]
def __contains__(self, key):
return key in self.d
def __missing__(self, key):
if self.default_factory is None:
raise KeyError(key)

Wyświetl plik

@ -0,0 +1,4 @@
srctype = micropython-lib
type = package
version = 0.2.1
author = Paul Sokolovsky

Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-collections.defaultdict',
version='0.2.1',
description='collections.defaultdict module for MicroPython',
long_description="This is a module reimplemented specifically for MicroPython standard library,\nwith efficient and lean design in mind. Note that this module is likely work\nin progress and likely supports just a subset of CPython's corresponding\nmodule. Please help with the development if you are interested in this\nmodule.",
url='https://github.com/micropython/micropython/issues/405',
author='Paul Sokolovsky',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
packages=['collections'])

Wyświetl plik

@ -1,10 +1,8 @@
from collections import defaultdict
d = defaultdict.defaultdict(lambda: 42)
d = defaultdict.defaultdict(lambda:42)
assert d[1] == 42
d[2] = 3
assert d[2] == 3
del d[1]
assert d[1] == 42
assert "foo" not in d

Wyświetl plik

@ -0,0 +1,37 @@
class deque:
def __init__(self, iterable=None):
if iterable is None:
self.q = []
else:
self.q = list(iterable)
def popleft(self):
return self.q.pop(0)
def popright(self):
return self.q.pop()
def pop(self):
return self.q.pop()
def append(self, a):
self.q.append(a)
def appendleft(self, a):
self.q.insert(0, a)
def extend(self, a):
self.q.extend(a)
def __len__(self):
return len(self.q)
def __bool__(self):
return bool(self.q)
def __iter__(self):
yield from self.q
def __str__(self):
return 'deque({})'.format(self.q)

Wyświetl plik

@ -0,0 +1,3 @@
srctype = micropython-lib
type = package
version = 0.1.2

Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-collections.deque',
version='0.1.2',
description='collections.deque module for MicroPython',
long_description="This is a module reimplemented specifically for MicroPython standard library,\nwith efficient and lean design in mind. Note that this module is likely work\nin progress and likely supports just a subset of CPython's corresponding\nmodule. Please help with the development if you are interested in this\nmodule.",
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
packages=['collections'])

Wyświetl plik

@ -0,0 +1,16 @@
# Should be reimplemented for MicroPython
# Reason:
# CPython implementation brings in metaclasses and other bloat.
# This is going to be just import-all for other modules in a namespace package
from ucollections import *
try:
from .defaultdict import defaultdict
except ImportError:
pass
try:
from .deque import deque
except ImportError:
pass
class MutableMapping:
pass

Wyświetl plik

@ -0,0 +1,3 @@
srctype = micropython-lib
type = package
version = 0.1.1

Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-collections',
version='0.1.1',
description='collections module for MicroPython',
long_description="This is a module reimplemented specifically for MicroPython standard library,\nwith efficient and lean design in mind. Note that this module is likely work\nin progress and likely supports just a subset of CPython's corresponding\nmodule. Please help with the development if you are interested in this\nmodule.",
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
packages=['collections'])

Wyświetl plik

@ -0,0 +1,3 @@
srctype=dummy
type=package
version=0.0.0

Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-concurrent.futures',
version='0.0.0',
description='Dummy concurrent.futures module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
packages=['concurrent'])

Wyświetl plik

@ -29,13 +29,10 @@ class closing(object):
f.close()
"""
def __init__(self, thing):
self.thing = thing
def __enter__(self):
return self.thing
def __exit__(self, *exc_info):
self.thing.close()
@ -69,7 +66,6 @@ class suppress:
# See http://bugs.python.org/issue12029 for more details
return exctype is not None and issubclass(exctype, self._exceptions)
# Inspired by discussions on http://bugs.python.org/issue13585
class ExitStack(object):
"""Context manager for dynamic management of a stack of exit callbacks
@ -83,23 +79,20 @@ class ExitStack(object):
# in the list raise an exception
"""
def __init__(self):
self._exit_callbacks = []
self._exit_callbacks = deque()
def pop_all(self):
"""Preserve the context stack by transferring it to a new instance"""
new_stack = type(self)()
new_stack._exit_callbacks = self._exit_callbacks
self._exit_callbacks = []
self._exit_callbacks = deque()
return new_stack
def _push_cm_exit(self, cm, cm_exit):
"""Helper to correctly register callbacks to __exit__ methods"""
def _exit_wrapper(*exc_details):
return cm_exit(cm, *exc_details)
self.push(_exit_wrapper)
def push(self, exit):
@ -120,19 +113,17 @@ class ExitStack(object):
self._exit_callbacks.append(exit)
else:
self._push_cm_exit(exit, exit_method)
return exit # Allow use as a decorator
return exit # Allow use as a decorator
def callback(self, callback, *args, **kwds):
"""Registers an arbitrary callback and arguments.
Cannot suppress exceptions.
"""
def _exit_wrapper(exc_type, exc, tb):
callback(*args, **kwds)
self.push(_exit_wrapper)
return callback # Allow use as a decorator
return callback # Allow use as a decorator
def enter_context(self, cm):
"""Enters the supplied context manager

Wyświetl plik

@ -0,0 +1,5 @@
srctype = cpython
type = module
version = 3.4.2-3
long_desc = Port of contextlib for micropython
depends = ucontextlib, collections

Wyświetl plik

@ -0,0 +1,19 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-contextlib',
version='3.4.2-3',
description='CPython contextlib module ported to MicroPython',
long_description='This is a module ported from CPython standard library to be compatible with\nMicroPython interpreter. Usually, this means applying small patches for\nfeatures not supported (yet, or at all) in MicroPython. Sometimes, heavier\nchanges are required. Note that CPython modules are written with availability\nof vast resources in mind, and may not work for MicroPython ports with\nlimited heap. If you are affected by such a case, please help reimplement\nthe module from scratch.',
url='https://github.com/micropython/micropython/issues/405',
author='CPython Developers',
author_email='python-dev@python.org',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='Python',
py_modules=['contextlib'],
install_requires=['micropython-ucontextlib', 'micropython-collections'])

Wyświetl plik

@ -4,6 +4,7 @@ from contextlib import closing, suppress, ExitStack
class ClosingTestCase(unittest.TestCase):
class Closable:
def __init__(self):
self.closed = False
@ -29,6 +30,7 @@ class ClosingTestCase(unittest.TestCase):
class SuppressTestCase(unittest.TestCase):
def test_suppress(self):
with suppress(ValueError, TypeError):
raise ValueError()
@ -37,6 +39,7 @@ class SuppressTestCase(unittest.TestCase):
class TestExitStack(unittest.TestCase):
# @support.requires_docstrings
def _test_instance_docs(self):
# Issue 19330: ensure context manager instances have good docstrings
@ -52,17 +55,15 @@ class TestExitStack(unittest.TestCase):
expected = [
((), {}),
((1,), {}),
((1, 2), {}),
((1,2), {}),
((), dict(example=1)),
((1,), dict(example=1)),
((1, 2), dict(example=1)),
((1,2), dict(example=1)),
]
result = []
def _exit(*args, **kwds):
"""Test metadata propagation"""
result.append((args, kwds))
with ExitStack() as stack:
for args, kwds in reversed(expected):
if args and kwds:
@ -82,28 +83,21 @@ class TestExitStack(unittest.TestCase):
def test_push(self):
exc_raised = ZeroDivisionError
def _expect_exc(exc_type, exc, exc_tb):
self.assertIs(exc_type, exc_raised)
def _suppress_exc(*exc_details):
return True
def _expect_ok(exc_type, exc, exc_tb):
self.assertIsNone(exc_type)
self.assertIsNone(exc)
self.assertIsNone(exc_tb)
class ExitCM(object):
def __init__(self, check_exc):
self.check_exc = check_exc
def __enter__(self):
self.fail("Should not be called!")
def __exit__(self, *exc_details):
self.check_exc(*exc_details)
with ExitStack() as stack:
stack.push(_expect_ok)
self.assertIs(tuple(stack._exit_callbacks)[-1], _expect_ok)
@ -119,24 +113,21 @@ class TestExitStack(unittest.TestCase):
self.assertIs(tuple(stack._exit_callbacks)[-1], _expect_exc)
stack.push(_expect_exc)
self.assertIs(tuple(stack._exit_callbacks)[-1], _expect_exc)
1 / 0
1/0
def test_enter_context(self):
class TestCM(object):
def __enter__(self):
result.append(1)
def __exit__(self, *exc_details):
result.append(3)
result = []
cm = TestCM()
with ExitStack() as stack:
@stack.callback # Registered first => cleaned up last
def _exit():
result.append(4)
self.assertIsNotNone(_exit)
stack.enter_context(cm)
# self.assertIs(stack._exit_callbacks[-1].__self__, cm)
@ -146,11 +137,9 @@ class TestExitStack(unittest.TestCase):
def test_close(self):
result = []
with ExitStack() as stack:
@stack.callback
def _exit():
result.append(1)
self.assertIsNotNone(_exit)
stack.close()
result.append(2)
@ -159,11 +148,9 @@ class TestExitStack(unittest.TestCase):
def test_pop_all(self):
result = []
with ExitStack() as stack:
@stack.callback
def _exit():
result.append(3)
self.assertIsNotNone(_exit)
new_stack = stack.pop_all()
result.append(1)
@ -175,25 +162,22 @@ class TestExitStack(unittest.TestCase):
with self.assertRaises(ZeroDivisionError):
with ExitStack() as stack:
stack.push(lambda *exc: False)
1 / 0
1/0
def test_exit_suppress(self):
with ExitStack() as stack:
stack.push(lambda *exc: True)
1 / 0
1/0
def test_exit_exception_chaining_reference(self):
# Sanity check to make sure that ExitStack chaining matches
# actual nested with statements
exc_chain = []
class RaiseExc:
def __init__(self, exc):
self.exc = exc
def __enter__(self):
return self
def __exit__(self, *exc_details):
exc_chain.append(exc_details[0])
raise self.exc
@ -202,10 +186,8 @@ class TestExitStack(unittest.TestCase):
def __init__(self, outer, inner):
self.outer = outer
self.inner = inner
def __enter__(self):
return self
def __exit__(self, *exc_details):
try:
exc_chain.append(exc_details[0])
@ -217,7 +199,6 @@ class TestExitStack(unittest.TestCase):
class SuppressExc:
def __enter__(self):
return self
def __exit__(self, *exc_details):
type(self).saved_details = exc_details
return True
@ -234,13 +215,7 @@ class TestExitStack(unittest.TestCase):
# Inner exceptions were suppressed
# self.assertIsNone(exc.__context__.__context__.__context__)
exc_chain.append(type(exc))
assert tuple(exc_chain) == (
ZeroDivisionError,
None,
AttributeError,
KeyError,
IndexError,
)
assert tuple(exc_chain) == (ZeroDivisionError, None, AttributeError, KeyError, IndexError)
else:
self.fail("Expected IndexError, but no exception was raised")
# Check the inner exceptions
@ -251,7 +226,6 @@ class TestExitStack(unittest.TestCase):
def test_exit_exception_chaining(self):
# Ensure exception chaining matches the reference behaviour
exc_chain = []
def raise_exc(exc):
frame_exc = sys.exc_info()[0]
if frame_exc is not None:
@ -260,7 +234,6 @@ class TestExitStack(unittest.TestCase):
raise exc
saved_details = None
def suppress_exc(*exc_details):
nonlocal saved_details
saved_details = exc_details
@ -281,13 +254,7 @@ class TestExitStack(unittest.TestCase):
# self.assertIsInstance(exc.__context__.__context__, AttributeError)
# Inner exceptions were suppressed
# self.assertIsNone(exc.__context__.__context__.__context__)
assert tuple(exc_chain) == (
ZeroDivisionError,
None,
AttributeError,
KeyError,
IndexError,
)
assert tuple(exc_chain) == (ZeroDivisionError, None, AttributeError, KeyError, IndexError)
else:
self.fail("Expected IndexError, but no exception was raised")
# Check the inner exceptions
@ -350,7 +317,8 @@ class TestExitStack(unittest.TestCase):
self.assertIs(exc.__context__, exc3)
self.assertIs(exc.__context__.__context__, exc2)
self.assertIs(exc.__context__.__context__.__context__, exc1)
self.assertIsNone(exc.__context__.__context__.__context__.__context__)
self.assertIsNone(
exc.__context__.__context__.__context__.__context__)
def _test_exit_exception_with_existing_context(self):
# Addresses a lack of test coverage discovered after checking in a
@ -360,7 +328,6 @@ class TestExitStack(unittest.TestCase):
raise inner_exc
finally:
raise outer_exc
exc1 = Exception(1)
exc2 = Exception(2)
exc3 = Exception(3)
@ -376,36 +343,37 @@ class TestExitStack(unittest.TestCase):
self.assertIs(exc.__context__, exc4)
self.assertIs(exc.__context__.__context__, exc3)
self.assertIs(exc.__context__.__context__.__context__, exc2)
self.assertIs(exc.__context__.__context__.__context__.__context__, exc1)
self.assertIsNone(exc.__context__.__context__.__context__.__context__.__context__)
self.assertIs(
exc.__context__.__context__.__context__.__context__, exc1)
self.assertIsNone(
exc.__context__.__context__.__context__.__context__.__context__)
def test_body_exception_suppress(self):
def suppress_exc(*exc_details):
return True
try:
with ExitStack() as stack:
stack.push(suppress_exc)
1 / 0
1/0
except IndexError as exc:
self.fail("Expected no exception, got IndexError")
def test_exit_exception_chaining_suppress(self):
with ExitStack() as stack:
stack.push(lambda *exc: True)
stack.push(lambda *exc: 1 / 0)
stack.push(lambda *exc: 1/0)
stack.push(lambda *exc: {}[1])
def test_excessive_nesting(self):
# The original implementation would die with RecursionError here
with ExitStack() as stack:
for i in range(5000):
for i in range(10000):
stack.callback(int)
def test_instance_bypass(self):
class Example(object):
pass
class Example(object): pass
cm = Example()
cm.__exit__ = object()
stack = ExitStack()
@ -414,5 +382,5 @@ class TestExitStack(unittest.TestCase):
self.assertIs(tuple(stack._exit_callbacks)[-1], cm)
if __name__ == "__main__":
if __name__ == '__main__':
unittest.main()

Wyświetl plik

@ -49,22 +49,13 @@ __getstate__() and __setstate__(). See the documentation for module
"""
import types
# import weakref
# from copyreg import dispatch_table
# import builtins
#import weakref
#from copyreg import dispatch_table
#import builtins
class Error(Exception):
pass
error = Error # backward compatibility
try:
from collections import OrderedDict
except ImportError:
OrderedDict = None
error = Error # backward compatibility
try:
from org.python.core import PyStringMap
@ -73,7 +64,6 @@ except ImportError:
__all__ = ["Error", "copy", "deepcopy"]
def copy(x):
"""Shallow copy operation on arbitrary Python objects.
@ -112,54 +102,33 @@ def copy(x):
_copy_dispatch = d = {}
def _copy_immutable(x):
return x
for t in (
type(None),
int,
float,
bool,
str,
tuple,
type,
range,
types.BuiltinFunctionType,
type(Ellipsis),
types.FunctionType,
):
for t in (type(None), int, float, bool, str, tuple,
type, range,
types.BuiltinFunctionType, type(Ellipsis),
types.FunctionType):
d[t] = _copy_immutable
t = getattr(types, "CodeType", None)
if t is not None:
d[t] = _copy_immutable
# for name in ("complex", "unicode"):
#for name in ("complex", "unicode"):
# t = getattr(builtins, name, None)
# if t is not None:
# d[t] = _copy_immutable
def _copy_with_constructor(x):
return type(x)(x)
for t in (list, dict, set):
d[t] = _copy_with_constructor
if OrderedDict is not None:
d[OrderedDict] = _copy_with_constructor
def _copy_with_copy_method(x):
return x.copy()
if PyStringMap is not None:
d[PyStringMap] = _copy_with_copy_method
del d
def deepcopy(x, memo=None, _nil=[]):
"""Deep copy operation on arbitrary Python objects.
@ -182,7 +151,7 @@ def deepcopy(x, memo=None, _nil=[]):
else:
try:
issc = issubclass(cls, type)
except TypeError: # cls is not a class (old Boost; see SF #502085)
except TypeError: # cls is not a class (old Boost; see SF #502085)
issc = 0
if issc:
y = _deepcopy_atomic(x, memo)
@ -203,23 +172,20 @@ def deepcopy(x, memo=None, _nil=[]):
if reductor:
rv = reductor()
else:
raise Error("un(deep)copyable object of type %s" % cls)
raise Error(
"un(deep)copyable object of type %s" % cls)
y = _reconstruct(x, rv, 1, memo)
# If is its own copy, don't memoize.
if y is not x:
memo[d] = y
_keep_alive(x, memo) # Make sure x lives at least as long as d
_keep_alive(x, memo) # Make sure x lives at least as long as d
return y
_deepcopy_dispatch = d = {}
def _deepcopy_atomic(x, memo):
return x
d[type(None)] = _deepcopy_atomic
d[type(Ellipsis)] = _deepcopy_atomic
d[int] = _deepcopy_atomic
@ -239,8 +205,7 @@ d[type] = _deepcopy_atomic
d[range] = _deepcopy_atomic
d[types.BuiltinFunctionType] = _deepcopy_atomic
d[types.FunctionType] = _deepcopy_atomic
# d[weakref.ref] = _deepcopy_atomic
#d[weakref.ref] = _deepcopy_atomic
def _deepcopy_list(x, memo):
y = []
@ -248,11 +213,8 @@ def _deepcopy_list(x, memo):
for a in x:
y.append(deepcopy(a, memo))
return y
d[list] = _deepcopy_list
def _deepcopy_tuple(x, memo):
y = []
for a in x:
@ -270,33 +232,22 @@ def _deepcopy_tuple(x, memo):
else:
y = x
return y
d[tuple] = _deepcopy_tuple
def _deepcopy_dict(x, memo):
y = type(x)()
y = {}
memo[id(x)] = y
for key, value in x.items():
y[deepcopy(key, memo)] = deepcopy(value, memo)
return y
d[dict] = _deepcopy_dict
if OrderedDict is not None:
d[OrderedDict] = _deepcopy_dict
if PyStringMap is not None:
d[PyStringMap] = _deepcopy_dict
def _deepcopy_method(x, memo): # Copy instance methods
def _deepcopy_method(x, memo): # Copy instance methods
return type(x)(x.__func__, deepcopy(x.__self__, memo))
_deepcopy_dispatch[types.MethodType] = _deepcopy_method
def _keep_alive(x, memo):
"""Keeps a reference to the object x in the memo.
@ -311,8 +262,7 @@ def _keep_alive(x, memo):
memo[id(memo)].append(x)
except KeyError:
# aha, this is the first one :-)
memo[id(memo)] = [x]
memo[id(memo)]=[x]
def _reconstruct(x, info, deep, memo=None):
if isinstance(info, str):
@ -343,7 +293,7 @@ def _reconstruct(x, info, deep, memo=None):
if state:
if deep:
state = deepcopy(state, memo)
if hasattr(y, "__setstate__"):
if hasattr(y, '__setstate__'):
y.__setstate__(state)
else:
if isinstance(state, tuple) and len(state) == 2:
@ -369,12 +319,10 @@ def _reconstruct(x, info, deep, memo=None):
y[key] = value
return y
del d
del types
# Helper for instance creation without calling __init__
class _EmptyClass:
pass

16
copy/setup.py 100644
Wyświetl plik

@ -0,0 +1,16 @@
import sys
# Remove current dir from sys.path, otherwise distutils will peek up our
# copy module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-copy',
version='0.0.2',
description='CPython copy module ported to MicroPython',
url='https://github.com/micropython/micropython/issues/405',
author='CPython Developers',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='Python',
install_requires=['micropython-types'],
py_modules=['copy'])

Wyświetl plik

@ -0,0 +1,21 @@
import uasyncio as asyncio
def run1():
for i in range(1):
print('Hello World')
yield from asyncio.sleep(2)
print("run1 finished")
def run2():
for i in range(3):
print('bar')
yield run1()
yield from asyncio.sleep(1)
import logging
logging.basicConfig(level=logging.INFO)
loop = asyncio.get_event_loop()
loop.create_task(run2())
loop.run_forever()

Wyświetl plik

@ -0,0 +1,3 @@
srctype = cpython-backport
type = module
version = 0.2

Wyświetl plik

@ -0,0 +1,27 @@
This patch shows changes done to asyncio.tasks.Task._step() from CPython 3.4.2.
--- tasks.py 2015-01-01 10:51:40.707114866 +0200
+++ uasyncio.py 2015-01-01 10:54:20.172402890 +0200
@@ -46,13 +55,16 @@
# Bare yield relinquishes control for one event loop iteration.
self._loop.call_soon(self._step)
elif inspect.isgenerator(result):
+ #print("Scheduling", result)
+ self._loop.create_task(result)
+ self._loop.call_soon(self._step)
# Yielding a generator is just wrong.
- self._loop.call_soon(
- self._step, None,
- RuntimeError(
- 'yield was used instead of yield from for '
- 'generator in task {!r} with {}'.format(
- self, result)))
+# self._loop.call_soon(
+# self._step, None,
+# RuntimeError(
+# 'yield was used instead of yield from for '
+# 'generator in task {!r} with {}'.format(
+# self, result)))
else:
# Yielding something else is an error.
self._loop.call_soon(

Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-cpython-uasyncio',
version='0.2',
description='MicroPython module uasyncio ported to CPython',
long_description='This is MicroPython compatibility module, allowing applications using\nMicroPython-specific features to run on CPython.\n',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='Python',
py_modules=['uasyncio'])

Wyświetl plik

@ -0,0 +1,99 @@
import inspect
import asyncio
import asyncio.futures as futures
from asyncio import *
OrgTask = Task
class Task(OrgTask):
def _step(self, value=None, exc=None):
assert not self.done(), \
'_step(): already done: {!r}, {!r}, {!r}'.format(self, value, exc)
if self._must_cancel:
if not isinstance(exc, futures.CancelledError):
exc = futures.CancelledError()
self._must_cancel = False
coro = self._coro
self._fut_waiter = None
self.__class__._current_tasks[self._loop] = self
# Call either coro.throw(exc) or coro.send(value).
try:
if exc is not None:
result = coro.throw(exc)
elif value is not None:
result = coro.send(value)
else:
result = next(coro)
except StopIteration as exc:
self.set_result(exc.value)
except futures.CancelledError as exc:
super().cancel() # I.e., Future.cancel(self).
except Exception as exc:
self.set_exception(exc)
except BaseException as exc:
self.set_exception(exc)
raise
else:
if isinstance(result, futures.Future):
# Yielded Future must come from Future.__iter__().
if result._blocking:
result._blocking = False
result.add_done_callback(self._wakeup)
self._fut_waiter = result
if self._must_cancel:
if self._fut_waiter.cancel():
self._must_cancel = False
else:
self._loop.call_soon(
self._step, None,
RuntimeError(
'yield was used instead of yield from '
'in task {!r} with {!r}'.format(self, result)))
elif result is None:
# Bare yield relinquishes control for one event loop iteration.
self._loop.call_soon(self._step)
elif inspect.isgenerator(result):
#print("Scheduling", result)
self._loop.create_task(result)
self._loop.call_soon(self._step)
# Yielding a generator is just wrong.
# self._loop.call_soon(
# self._step, None,
# RuntimeError(
# 'yield was used instead of yield from for '
# 'generator in task {!r} with {}'.format(
# self, result)))
else:
# Yielding something else is an error.
self._loop.call_soon(
self._step, None,
RuntimeError(
'Task got bad yield: {!r}'.format(result)))
finally:
self.__class__._current_tasks.pop(self._loop)
self = None # Needed to break cycles when an exception occurs.
asyncio.tasks.Task = Task
OrgStreamWriter = StreamWriter
class StreamWriter(OrgStreamWriter):
def awrite(self, data):
if isinstance(data, str):
data = data.encode("utf-8")
self.write(data)
yield from self.drain()
def aclose(self):
self.close()
return
yield
asyncio.streams.StreamWriter = StreamWriter

0
csv/csv.py 100644
Wyświetl plik

3
csv/metadata.txt 100644
Wyświetl plik

@ -0,0 +1,3 @@
srctype = dummy
type = module
version = 0.0.0

18
csv/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-csv',
version='0.0.0',
description='Dummy csv module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['csv'])

Wyświetl plik

@ -0,0 +1,99 @@
"""Constants and membership tests for ASCII characters"""
NUL = 0x00 # ^@
SOH = 0x01 # ^A
STX = 0x02 # ^B
ETX = 0x03 # ^C
EOT = 0x04 # ^D
ENQ = 0x05 # ^E
ACK = 0x06 # ^F
BEL = 0x07 # ^G
BS = 0x08 # ^H
TAB = 0x09 # ^I
HT = 0x09 # ^I
LF = 0x0a # ^J
NL = 0x0a # ^J
VT = 0x0b # ^K
FF = 0x0c # ^L
CR = 0x0d # ^M
SO = 0x0e # ^N
SI = 0x0f # ^O
DLE = 0x10 # ^P
DC1 = 0x11 # ^Q
DC2 = 0x12 # ^R
DC3 = 0x13 # ^S
DC4 = 0x14 # ^T
NAK = 0x15 # ^U
SYN = 0x16 # ^V
ETB = 0x17 # ^W
CAN = 0x18 # ^X
EM = 0x19 # ^Y
SUB = 0x1a # ^Z
ESC = 0x1b # ^[
FS = 0x1c # ^\
GS = 0x1d # ^]
RS = 0x1e # ^^
US = 0x1f # ^_
SP = 0x20 # space
DEL = 0x7f # delete
controlnames = [
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
"BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
"CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US",
"SP"
]
def _ctoi(c):
if type(c) == type(""):
return ord(c)
else:
return c
def isalnum(c): return isalpha(c) or isdigit(c)
def isalpha(c): return isupper(c) or islower(c)
def isascii(c): return _ctoi(c) <= 127 # ?
def isblank(c): return _ctoi(c) in (8,32)
def iscntrl(c): return _ctoi(c) <= 31
def isdigit(c): return _ctoi(c) >= 48 and _ctoi(c) <= 57
def isgraph(c): return _ctoi(c) >= 33 and _ctoi(c) <= 126
def islower(c): return _ctoi(c) >= 97 and _ctoi(c) <= 122
def isprint(c): return _ctoi(c) >= 32 and _ctoi(c) <= 126
def ispunct(c): return _ctoi(c) != 32 and not isalnum(c)
def isspace(c): return _ctoi(c) in (9, 10, 11, 12, 13, 32)
def isupper(c): return _ctoi(c) >= 65 and _ctoi(c) <= 90
def isxdigit(c): return isdigit(c) or \
(_ctoi(c) >= 65 and _ctoi(c) <= 70) or (_ctoi(c) >= 97 and _ctoi(c) <= 102)
def isctrl(c): return _ctoi(c) < 32
def ismeta(c): return _ctoi(c) > 127
def ascii(c):
if type(c) == type(""):
return chr(_ctoi(c) & 0x7f)
else:
return _ctoi(c) & 0x7f
def ctrl(c):
if type(c) == type(""):
return chr(_ctoi(c) & 0x1f)
else:
return _ctoi(c) & 0x1f
def alt(c):
if type(c) == type(""):
return chr(_ctoi(c) | 0x80)
else:
return _ctoi(c) | 0x80
def unctrl(c):
bits = _ctoi(c)
if bits == 0x7f:
rep = "^?"
elif isprint(bits & 0x7f):
rep = chr(bits & 0x7f)
else:
rep = "^" + chr(((bits & 0x7f) | 0x20) + 0x20)
if bits & 0x80:
return "!" + rep
return rep

Wyświetl plik

@ -0,0 +1,3 @@
srctype = cpython
type = package
version = 3.4.2

Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-curses.ascii',
version='3.4.2',
description='CPython curses.ascii module ported to MicroPython',
long_description='This is a module ported from CPython standard library to be compatible with\nMicroPython interpreter. Usually, this means applying small patches for\nfeatures not supported (yet, or at all) in MicroPython. Sometimes, heavier\nchanges are required. Note that CPython modules are written with availability\nof vast resources in mind, and may not work for MicroPython ports with\nlimited heap. If you are affected by such a case, please help reimplement\nthe module from scratch.',
url='https://github.com/micropython/micropython/issues/405',
author='CPython Developers',
author_email='python-dev@python.org',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='Python',
packages=['curses'])

Wyświetl plik

Wyświetl plik

@ -0,0 +1,3 @@
srctype=dummy
type=module
version=0.0.1

18
datetime/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-datetime',
version='0.0.1',
description='Dummy datetime module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['datetime'])

0
dbm/dbm.py 100644
Wyświetl plik

3
dbm/metadata.txt 100644
Wyświetl plik

@ -0,0 +1,3 @@
srctype=dummy
type=module
version=0.0.1

18
dbm/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-dbm',
version='0.0.1',
description='Dummy dbm module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['dbm'])

Wyświetl plik

Wyświetl plik

@ -0,0 +1,3 @@
srctype=dummy
type=module
version=0.0.1

18
decimal/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-decimal',
version='0.0.1',
description='Dummy decimal module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['decimal'])

Wyświetl plik

Wyświetl plik

@ -0,0 +1,3 @@
srctype=dummy
type=module
version=0.0.1

18
difflib/setup.py 100644
Wyświetl plik

@ -0,0 +1,18 @@
import sys
# Remove current dir from sys.path, otherwise setuptools will peek up our
# module instead of system.
sys.path.pop(0)
from setuptools import setup
setup(name='micropython-difflib',
version='0.0.1',
description='Dummy difflib module for MicroPython',
long_description='This is a dummy implementation of a module for MicroPython standard library.\nIt contains zero or very little functionality, and primarily intended to\navoid import errors (using idea that even if an application imports a\nmodule, it may be not using it onevery code path, so may work at least\npartially). It is expected that more complete implementation of the module\nwill be provided later. Please help with the development if you are\ninterested in this module.',
url='https://github.com/micropython/micropython/issues/405',
author='MicroPython Developers',
author_email='micro-python@googlegroups.com',
maintainer='MicroPython Developers',
maintainer_email='micro-python@googlegroups.com',
license='MIT',
py_modules=['difflib'])

Wyświetl plik

@ -3,11 +3,11 @@
# Contact: email-sig@python.org
__all__ = [
"Charset",
"add_alias",
"add_charset",
"add_codec",
]
'Charset',
'add_alias',
'add_charset',
'add_codec',
]
from functools import partial
@ -18,90 +18,93 @@ from email import errors
from email.encoders import encode_7or8bit
# Flags for types of header encodings
QP = 1 # Quoted-Printable
BASE64 = 2 # Base64
SHORTEST = 3 # the shorter of QP and base64, but only for headers
QP = 1 # Quoted-Printable
BASE64 = 2 # Base64
SHORTEST = 3 # the shorter of QP and base64, but only for headers
# In "=?charset?q?hello_world?=", the =?, ?q?, and ?= add up to 7
RFC2047_CHROME_LEN = 7
DEFAULT_CHARSET = "us-ascii"
UNKNOWN8BIT = "unknown-8bit"
EMPTYSTRING = ""
DEFAULT_CHARSET = 'us-ascii'
UNKNOWN8BIT = 'unknown-8bit'
EMPTYSTRING = ''
# Defaults
CHARSETS = {
# input header enc body enc output conv
"iso-8859-1": (QP, QP, None),
"iso-8859-2": (QP, QP, None),
"iso-8859-3": (QP, QP, None),
"iso-8859-4": (QP, QP, None),
'iso-8859-1': (QP, QP, None),
'iso-8859-2': (QP, QP, None),
'iso-8859-3': (QP, QP, None),
'iso-8859-4': (QP, QP, None),
# iso-8859-5 is Cyrillic, and not especially used
# iso-8859-6 is Arabic, also not particularly used
# iso-8859-7 is Greek, QP will not make it readable
# iso-8859-8 is Hebrew, QP will not make it readable
"iso-8859-9": (QP, QP, None),
"iso-8859-10": (QP, QP, None),
'iso-8859-9': (QP, QP, None),
'iso-8859-10': (QP, QP, None),
# iso-8859-11 is Thai, QP will not make it readable
"iso-8859-13": (QP, QP, None),
"iso-8859-14": (QP, QP, None),
"iso-8859-15": (QP, QP, None),
"iso-8859-16": (QP, QP, None),
"windows-1252": (QP, QP, None),
"viscii": (QP, QP, None),
"us-ascii": (None, None, None),
"big5": (BASE64, BASE64, None),
"gb2312": (BASE64, BASE64, None),
"euc-jp": (BASE64, None, "iso-2022-jp"),
"shift_jis": (BASE64, None, "iso-2022-jp"),
"iso-2022-jp": (BASE64, None, None),
"koi8-r": (BASE64, BASE64, None),
"utf-8": (SHORTEST, BASE64, "utf-8"),
}
'iso-8859-13': (QP, QP, None),
'iso-8859-14': (QP, QP, None),
'iso-8859-15': (QP, QP, None),
'iso-8859-16': (QP, QP, None),
'windows-1252':(QP, QP, None),
'viscii': (QP, QP, None),
'us-ascii': (None, None, None),
'big5': (BASE64, BASE64, None),
'gb2312': (BASE64, BASE64, None),
'euc-jp': (BASE64, None, 'iso-2022-jp'),
'shift_jis': (BASE64, None, 'iso-2022-jp'),
'iso-2022-jp': (BASE64, None, None),
'koi8-r': (BASE64, BASE64, None),
'utf-8': (SHORTEST, BASE64, 'utf-8'),
}
# Aliases for other commonly-used names for character sets. Map
# them to the real ones used in email.
ALIASES = {
"latin_1": "iso-8859-1",
"latin-1": "iso-8859-1",
"latin_2": "iso-8859-2",
"latin-2": "iso-8859-2",
"latin_3": "iso-8859-3",
"latin-3": "iso-8859-3",
"latin_4": "iso-8859-4",
"latin-4": "iso-8859-4",
"latin_5": "iso-8859-9",
"latin-5": "iso-8859-9",
"latin_6": "iso-8859-10",
"latin-6": "iso-8859-10",
"latin_7": "iso-8859-13",
"latin-7": "iso-8859-13",
"latin_8": "iso-8859-14",
"latin-8": "iso-8859-14",
"latin_9": "iso-8859-15",
"latin-9": "iso-8859-15",
"latin_10": "iso-8859-16",
"latin-10": "iso-8859-16",
"cp949": "ks_c_5601-1987",
"euc_jp": "euc-jp",
"euc_kr": "euc-kr",
"ascii": "us-ascii",
}
'latin_1': 'iso-8859-1',
'latin-1': 'iso-8859-1',
'latin_2': 'iso-8859-2',
'latin-2': 'iso-8859-2',
'latin_3': 'iso-8859-3',
'latin-3': 'iso-8859-3',
'latin_4': 'iso-8859-4',
'latin-4': 'iso-8859-4',
'latin_5': 'iso-8859-9',
'latin-5': 'iso-8859-9',
'latin_6': 'iso-8859-10',
'latin-6': 'iso-8859-10',
'latin_7': 'iso-8859-13',
'latin-7': 'iso-8859-13',
'latin_8': 'iso-8859-14',
'latin-8': 'iso-8859-14',
'latin_9': 'iso-8859-15',
'latin-9': 'iso-8859-15',
'latin_10':'iso-8859-16',
'latin-10':'iso-8859-16',
'cp949': 'ks_c_5601-1987',
'euc_jp': 'euc-jp',
'euc_kr': 'euc-kr',
'ascii': 'us-ascii',
}
# Map charsets to their Unicode codec strings.
CODEC_MAP = {
"gb2312": "eucgb2312_cn",
"big5": "big5_tw",
'gb2312': 'eucgb2312_cn',
'big5': 'big5_tw',
# Hack: We don't want *any* conversion for stuff marked us-ascii, as all
# sorts of garbage might be sent to us in the guise of 7-bit us-ascii.
# Let that stuff pass through without conversion to/from Unicode.
"us-ascii": None,
}
'us-ascii': None,
}
# Convenience functions for extending the above mappings
def add_charset(charset, header_enc=None, body_enc=None, output_charset=None):
"""Add character set properties to the global registry.
@ -127,7 +130,7 @@ def add_charset(charset, header_enc=None, body_enc=None, output_charset=None):
documentation for more information.
"""
if body_enc == SHORTEST:
raise ValueError("SHORTEST not allowed for body_enc")
raise ValueError('SHORTEST not allowed for body_enc')
CHARSETS[charset] = (header_enc, body_enc, output_charset)
@ -150,15 +153,17 @@ def add_codec(charset, codecname):
CODEC_MAP[charset] = codecname
# Convenience function for encoding strings, taking into account
# that they might be unknown-8bit (ie: have surrogate-escaped bytes)
def _encode(string, codec):
if codec == UNKNOWN8BIT:
return string.encode("ascii", "surrogateescape")
return string.encode('ascii', 'surrogateescape')
else:
return string.encode(codec)
class Charset:
"""Map character sets to their email properties.
@ -203,7 +208,6 @@ class Charset:
to the output_charset. If no conversion codec is necessary,
this attribute will have the same value as the input_codec.
"""
def __init__(self, input_charset=DEFAULT_CHARSET):
# RFC 2046, $4.1.2 says charsets are not case sensitive. We coerce to
# unicode because its .lower() is locale insensitive. If the argument
@ -211,9 +215,9 @@ class Charset:
# charset is ASCII, as the standard (RFC XXX) requires.
try:
if isinstance(input_charset, str):
input_charset.encode("ascii")
input_charset.encode('ascii')
else:
input_charset = str(input_charset, "ascii")
input_charset = str(input_charset, 'ascii')
except UnicodeError:
raise errors.CharsetError(input_charset)
input_charset = input_charset.lower()
@ -222,7 +226,8 @@ class Charset:
# We can try to guess which encoding and conversion to use by the
# charset_map dictionary. Try that first, but let the user override
# it.
henc, benc, conv = CHARSETS.get(self.input_charset, (SHORTEST, BASE64, None))
henc, benc, conv = CHARSETS.get(self.input_charset,
(SHORTEST, BASE64, None))
if not conv:
conv = self.input_charset
# Set the attributes, allowing the arguments to override the default.
@ -231,8 +236,10 @@ class Charset:
self.output_charset = ALIASES.get(conv, conv)
# Now set the codecs. If one isn't defined for input_charset,
# guess and try a Unicode codec with the same name as input_codec.
self.input_codec = CODEC_MAP.get(self.input_charset, self.input_charset)
self.output_codec = CODEC_MAP.get(self.output_charset, self.output_charset)
self.input_codec = CODEC_MAP.get(self.input_charset,
self.input_charset)
self.output_codec = CODEC_MAP.get(self.output_charset,
self.output_charset)
def __str__(self):
return self.input_charset.lower()
@ -260,9 +267,9 @@ class Charset:
"""
assert self.body_encoding != SHORTEST
if self.body_encoding == QP:
return "quoted-printable"
return 'quoted-printable'
elif self.body_encoding == BASE64:
return "base64"
return 'base64'
else:
return encode_7or8bit
@ -285,7 +292,7 @@ class Charset:
output codec.
:return: The encoded string, with RFC 2047 chrome.
"""
codec = self.output_codec or "us-ascii"
codec = self.output_codec or 'us-ascii'
header_bytes = _encode(string, codec)
# 7bit/8bit encodings return the string unchanged (modulo conversions)
encoder_module = self._get_encoder(header_bytes)
@ -311,7 +318,7 @@ class Charset:
:return: Lines of encoded strings, each with RFC 2047 chrome.
"""
# See which encoding we should use.
codec = self.output_codec or "us-ascii"
codec = self.output_codec or 'us-ascii'
header_bytes = _encode(string, codec)
encoder_module = self._get_encoder(header_bytes)
encoder = partial(encoder_module.header_encode, charset=codec)
@ -344,7 +351,7 @@ class Charset:
if not lines and not current_line:
lines.append(None)
else:
separator = " " if lines else ""
separator = (' ' if lines else '')
joined_line = EMPTYSTRING.join(current_line)
header_bytes = _encode(joined_line, codec)
lines.append(encoder(header_bytes))
@ -397,9 +404,9 @@ class Charset:
# being bytes has never been nailed down, so fixing that is a
# longer term TODO.
if isinstance(string, str):
string = string.encode(self.output_charset).decode("latin1")
string = string.encode(self.output_charset).decode('latin1')
return email.quoprimime.body_encode(string)
else:
if isinstance(string, str):
string = string.encode(self.output_charset).decode("ascii")
string = string.encode(self.output_charset).decode('ascii')
return string

Some files were not shown because too many files have changed in this diff Show More