pax_global_header 0000666 0000000 0000000 00000000064 14512304031 0014503 g ustar 00root root 0000000 0000000 52 comment=a9eac3179bfa12f74685607d85471ab2c601dad4
xphyle-4.4.4/ 0000775 0000000 0000000 00000000000 14512304031 0013025 5 ustar 00root root 0000000 0000000 xphyle-4.4.4/.coveragerc 0000664 0000000 0000000 00000000315 14512304031 0015145 0 ustar 00root root 0000000 0000000 [run]
omit =
tests/*
setup.py
*site-packages/*
[report]
exclude_lines =
pragma: no cover
pragma: no-cover
def __repr__
raise NotImplementedError
if __name__ == .__main__.:
xphyle-4.4.4/.gitattributes 0000664 0000000 0000000 00000000040 14512304031 0015712 0 ustar 00root root 0000000 0000000 xphyle/_version.py export-subst
xphyle-4.4.4/.gitignore 0000664 0000000 0000000 00000000164 14512304031 0015016 0 ustar 00root root 0000000 0000000 .cache
.coverage
build/
dist/
*.pyc
xphyle.egg-info/
docs/_build/
.vscode*
.mypy_cache/
.idea/
.pytest_cache/
.eggs/ xphyle-4.4.4/.pylintrc 0000664 0000000 0000000 00000001145 14512304031 0014673 0 ustar 00root root 0000000 0000000 [MESSAGES CONTROL]
# C0303: Trailing whitespace; will add a custom checker that only flags
# trailing whitespace at the end of non-empty lines
# C0326: Disabled because it incorrectly flags whitespace around default values
# when function annotations are used; will add a custom checker that
# flags all other cases
disable=fixme,C0303,C0326,too-few-public-methods,too-many-instance-attributes,too-many-arguments,too-many-locals,too-many-branches,too-many-statements,too-many-function-args,too-many-lines,too-many-boolean-expressions,too-many-return-statements
ignore=__pycache__,_version.py
xphyle-4.4.4/.travis.yml 0000664 0000000 0000000 00000000530 14512304031 0015134 0 ustar 00root root 0000000 0000000 sudo: false
language: python
cache:
directories:
- $HOME/.cache/pip
os:
- linux
python:
- 3.6
- 3.7
- 3.8
- 3.9
- 3.10
install:
- pip install --upgrade pip wheel
- pip install pytest-cov
- pip install coveralls
- pip install pylint
- make install
script:
- make test
after_success:
- coveralls
- pylint xphyle
xphyle-4.4.4/CHANGES.md 0000664 0000000 0000000 00000015574 14512304031 0014433 0 ustar 00root root 0000000 0000000 # Changes
## v4.4.1 (2020.12.06)
* Fix #41 - Windows does not support SIGPIPE
## v4.4.0 (2020.08.27)
* Add support for memory mapping, using the `memory_map` argument to `open_`/`xopen`
## v4.3.0 (2020.07.30)
* Add support for igzip
* Remove useless -p argument when decompressing with pigz
## v4.2.2 (2020.01.02)
* Handle differences between gzip and pigz -l output
## v4.2.1 (2019.12.13)
* Switch from versioneer to setup_tools_scm for version managment.
## v4.2.0 (2019.11.20)
* Add `xphyle.get_compressor`
* Fix Python 3.8 issue with importing from collections
## v4.1.3 (2019.10.09)
* Fixed issue with opening bgzip files
## v4.1.2 (2019.06.14)
* Correctly handle file modes when detecting placeholders
## v4.1.1 (2019.06.14)
* Correctly handle placeholder strings ('-', '_') as arguments to xopen
## v4.1.0 (2019.06.14)
* Add support for zstd
* Adjusted default compression levels based on benchmarking of compression tools.
* Handle placeholder strings ('-', '_') as arguments to xopen
## v4.0.8 (2019.04.08)
* Add pathlib.PurePath as a member of the PathLike type, to work around the lack of os.PathLike as a static superclass of PurePath in python 3.6
## v4.0.7 (2019.04.06)
* Don't complain when writing a bgzip file and the extension is gz
* Reformat codebase using black, and other code cleanup
## v4.0.5 (2019.01.10)
* Fix setup.py and Makefile to perform pypi upload correctly
* Add readthedocs.yml and update docs config to get docs building correctly
## v4.0.0 (2019.01.10)
* Official 4.0.0 release
## v4.0.0-rc1 (2018.08.02)
* Support non-.gz extensions when decompressing bgzip files.
## v4.0.0-rc0 (2018.03.18)
* Starting with v4, xphyle requires python 3.6+
* All path-oriented functions now use pathlib paths by default. Support for string paths is deprecated.
* Moved to pokrok for progress bar management.
## v3.1.6 (2018.01.16)
* Fix bug when specifying file_type=FileType.FILELIKE.
## v3.1.5 (2017.12.11)
* Added `close_fileobj` parameters to `xopen()` to allow user to specify whether the file/buffer should be closed when the wrapper is closed.
## v3.1.2 (2017.11.18)
* Added `xphyle.utils.uncompressed_size()`.
## v3.1.1 (2017.10.13)
* Added 'overwrite' parameter to xopen (defaults to True).
## v3.1.0 (2017.08.31)
* *Possible breaking change*: We discovered that python 3.3 support never fully worked due to some incompatibilities in the backported libraries for features we rely on that were introduced in 3.4. Thus, we are officially dropping support for python 3.3. This also reverts the change made in 3.0.7.
* Please ignore releases 3.0.8 and 3.0.9.
## v3.0.7 (2017.07.22)
* Add missing pathlib backport dependency for py3.3.
## v3.0.6 (2017.07.22)
* Added 'list_extensions' method to xphyle.formats.Formats.
* Fixed subtle bug that would cause failure when calling xopen on stdout that has been monkeypatched (as is done by pytest).
## v3.0.5 (2017.07.19)
* Fixed #13: opening corrupt gzip file fails silently.
## v3.0.3 (2017.06.14)
* Added basic performance testing.
* Fixed #12: xphyle not recognizing when system-level lzma not installed.
## v3.0.2 (2017.05.23)
* Forcing use of backports.typing for python < 3.6.
## v3.0.1 (2017.04.29)
* Added a paper for submission to JOSS.
* Enabled DOI generation using Zenodo.
## v3.0.0 (2017.04.18)
* Lots of fixes for bugs and type errors using mypy.
* Two breakting changes that necessitate the major version bump:
* Several methods were erroneously named "uncompress_..." and have been corrected to "decompress_..."
* Default values were erroneously used for the char_mode and linesep parameters of fileinput(), fileoutput(), FileInput, FileOutput, and all their subclasses. textinput(), textoutput(), byteinput(), and byteoutput() convenience methods were added, and default values were set to None.
## v2.2.3 (2017.04.09)
* Add get_compression_format_name() method to Formats.
* Validate the compression type in xopen.
## v2.2.1 (2017.03.01)
* Switch to pytest for testing.
* Bugfixes in fileoutput.
* Add ability to specifiy a file header for each file opened by fileoutput.
* Add ability to pass initializing text/bytes to xopen with file_type==BUFFER to create a readable buffer.
## v2.2.0 (2017.02.17)
* Add caching for FileMode and PermissionSet
* Add PatternFileOutput subclass of FileOuptut for generating output files from a pattern and tokens derived from lines in the file.
## v2.1.1 (2017.02.13)
* Minor bug fixes
* Code cleanup (thanks to Codacy)
## v2.1.0 (2017.02.11)
* Added support for opening buffer types.
## v2.0.0 (2017.02.11)
* The major version change reflects the introduction of potentially breaking changes:
1. When a file object is passed to `open_`, it is now wrapped in a `FileLikeWrapper` by default. To avoid this behavior, set `wrap_fileobj=False`, but note that if the file-like object is not a context manager, an error will be raised.
2. `xopen` no longer wraps files in `FileLikeWrapper` by default. To revert to the old behavior, set `xphyle.configure(default_xopen_context_wrapper=True)`.
3. For several methods in the `xphyle.paths` module, the `mode` argument has been renamed to `access` to avoid ambiguity.
4. `xphyle.paths.check_writeable_file` and `xphyle.paths.safe_check_writeable_file` have been changed to 'writable' to be consistent with the spelling used in core python.
5. In the `xphyle.paths` module:
* `check_file_mode` is removed.
* `get_access` is renamed to `get_permissions`.
* Many attribute and method names changed, mostly due to renaming of 'access' to 'permissions'.
6. In the context of `FileInput`, `mode` parameters have been changed to `char_mode`.
7. The `is_iterable` method has moved from `xphyle.utils` to `xphyle.types`.
8. The `types` parameter of `xphyle.utils.find` is renamed to path_types.
9. The string name of the FIFO path type has changed from 'fifo' to '|'.
* Added `xphyle.popen`, which opens subprocesses (i.e. `subprocess.Popen` instances) and uses `xopen` to open stdin/stdout/sterr files or wrap PIPEs. This enables sending compressed data to/reading compressed data from subprocesses without knowing in advance what the compression format will be or whether native compression/decompression programs are available.
* `xopen` now accepts two additional argument types: file objects and system commands. The later are specified as a string beginning with '|' (similar to the toolshed `nopen` method). PIPEs are automatically opened for stdin, stdout, and stderr. Additionally, if a compression type is specified, it is used to wrap one of the pipes as follows:
* If mode is read or readwrite, `xopen` opens a PIPE to stdout.
* Otherwise, `xopen` opens a PIPE to stdin.
* Enumerated types are now provided (in `xphyle.typing`) for all argument types in which fixed sets of strings were used previously (e.g. file open mode, path type). All methods with these argument types now accept either the string or Enum value.
xphyle-4.4.4/CITATION 0000664 0000000 0000000 00000000755 14512304031 0014171 0 ustar 00root root 0000000 0000000 Didion, JP (2017) xphyle: Extraordinarily simple file handling. Journal of Open Source Software; [doi:10.21105/joss.00255](https://doi.org/10.21105/joss.00255)
@article{Didion2017,
doi = {10.21105/joss.00255},
url = {https://doi.org/10.21105/joss.00255},
year = {2017},
publisher = {The Open Journal},
volume = {2},
number = {14},
pages = {255},
author = {John Didion},
title = {xphyle: Extraordinarily simple file handling},
journal = {Journal of Open Source Software}
}
xphyle-4.4.4/CODE_OF_CONDUCT.md 0000664 0000000 0000000 00000006236 14512304031 0015633 0 ustar 00root root 0000000 0000000 # Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
xphyle-4.4.4/CONTRIBUTING.md 0000664 0000000 0000000 00000001442 14512304031 0015257 0 ustar 00root root 0000000 0000000 We welcome any contributions via pull requests. We are especially interested in a collaborator that would either backport xphyle to Python 2.x or implement a compatibility layer to my xphyle Python version-independent.
All code must be written in idiomatic python 3. Note that we use [PEP484](https://www.python.org/dev/peps/pep-0484/) type hints. Variable annotations are
defined where needed using the comment syntax. Static code analysis is performed usying [mypy](http://mypy-lang.org/) and pylint.
Style-wise, we try to adhere to the Google python style guidelines. We use Google-style docstrings, which are formatted by the [Napoleon Sphinx Plugin](https://pypi.python.org/pypi/sphinxcontrib-napoleon).
We enforce the [Contributor Covenant](http://contributor-covenant.org/) code of conduct.
xphyle-4.4.4/LICENSE 0000664 0000000 0000000 00000003317 14512304031 0014036 0 ustar 00root root 0000000 0000000 **MIT License**
Copyright 2017 John P Didion
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO E
VENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
**Copyrighted Works**
This software may use copyrighted and/or public domain works and distributes
these works under the terms of their respective licenses. All copyright
restrictions still apply to these 'third-party' packages. Furthermore, xphyle is
a community project with contributors within and outside of the US Government;
these authors retain copyright on their work, which they may relinquish via a
public domain dedication. Below is a list of contributors, and either the
license under which their work is governed, or the release of copyright under
public domain dedication.
**List of Contributors**
John P Didion 2016-2017 Public Domain
xphyle-4.4.4/MANIFEST.in 0000664 0000000 0000000 00000000244 14512304031 0014563 0 ustar 00root root 0000000 0000000 include README.md
include LICENSE
include tests/test*.py
include docs/*.rst
include docs/conf.py
include docs/Makefile
include docs/logo.png
include docs/api/*.rst
xphyle-4.4.4/Makefile 0000664 0000000 0000000 00000002544 14512304031 0014472 0 ustar 00root root 0000000 0000000 module = xphyle
#pytestops = "--full-trace"
#pytestops = "-v -s"
repo = jdidion/$(module)
desc = Release $(version)
tests = tests
desc = ''
# Use this option to show full stack trace for errors
#pytestopts = "--full-trace"
all: install test
install:
python setup.py install
test:
pytest -m "not perf" -vv --cov --cov-report term-missing $(pytestopts) $(tests)
perftest:
pytest -m "perf" $(tests)
clean:
rm -Rf __pycache__
rm -Rf **/__pycache__/*
rm -Rf dist
rm -Rf build
rm -Rf *.egg-info
rm -Rf .pytest_cache
rm -Rf .coverage
tag:
git tag $(version)
release: clean tag install test
echo "Releasing version $(version)"
python setup.py sdist bdist_wheel
# pypi doesn't accept eggs
rm dist/*.egg
# release
#python setup.py upload -r pypi
twine upload -u "__token__" -p "$(pypi_token)" dist/*
# push new tag after successful build
git push origin --tags
# create release in GitHub
curl -v -i -X POST \
-H "Content-Type:application/json" \
-H "Authorization: token $(token)" \
https://api.github.com/repos/$(repo)/releases \
-d '{ \
"tag_name":"$(version)", \
"target_commitish": "master", \
"name": "$(version)", \
"body": "$(desc)", \
"draft": false, \
"prerelease": false \
}'
docs:
make -C docs api
make -C docs html
readme:
pandoc --from=markdown --to=rst --output=README.rst README.md
lint:
pylint $(module)
xphyle-4.4.4/README.md 0000664 0000000 0000000 00000007315 14512304031 0014312 0 ustar 00root root 0000000 0000000 # xphyle: extraordinarily simple file handling
[](https://pypi.python.org/pypi/xphyle)
[](https://travis-ci.org/jdidion/xphyle)
[](https://coveralls.io/github/jdidion/xphyle?branch=master)
[](https://www.codacy.com/app/jdidion/xphyle?utm_source=github.com&utm_medium=referral&utm_content=jdidion/xphyle&utm_campaign=Badge_Grade)
[](http://xphyle.readthedocs.io/en/latest/?badge=latest)
[](https://zenodo.org/badge/latestdoi/71260678)
[](http://joss.theoj.org/papers/10.21105/joss.00255)
xphyle is a small python library that makes it easy to open compressed
files. Most importantly, xphyle will use the appropriate program (e.g. 'gzip') to compress/decompress a file if it is available on your system; this is almost always faster than using the corresponding python library. xphyle also provides methods that simplify common file I/O operations.
Recent version of xphyle (4.0.0+) require python 3.6. Older versions of xphyle support python 3.4+.
Please note that xphyle may work on Windows, but it is not tested.
# Installation
```
pip install xphyle
```
# Building from source
Clone this repository and run
```
make
```
# Example usages:
```python
from xphyle import *
from xphyle.paths import STDIN, STDOUT
# Open a compressed file...
myfile = xopen('infile.gz')
# ...or a compressed stream
# e.g. gzip -c afile | python my_program.py
stdin = xopen(STDIN)
# Easily write to the stdin of a subprocess
with open_('|cat', 'wt') as process:
process.write('foo')
# We have to tell xopen what kind of compression
# to use when writing to stdout
stdout = xopen(STDOUT, compression='gz')
# The `open_` method ensures that the file is usable with the `with` keyword.
# Print all lines in a compressed file...
with open_('infile.gz') as myfile:
for line in myfile:
print(line)
# ... or a compressed URL
with open_('http://foo.com/myfile.gz') as myfile:
for line in myfile:
print(line)
# Transparently handle paths and file objects
def dostuff(path_or_file):
with open_(path_or_file) as myfile:
for line in myfile:
print(line)
# Read all lines in a compressed file into a list
from xphyle.utils import read_lines
lines = list(read_lines('infile.gz'))
# Sum the rows in a compressed file where each line is an integer value
total = sum(read_lines('infile.gz', convert=int))
```
See the [Documentation](https://xphyle.readthedocs.io/en/latest/) for full usage information.
# Supported compression formats
* `gzip` (uses `igzip` or `pigz` if available)
* `bgzip`
* `bzip2` (uses `pbzip2` if available)
* `lzma`
* `zstd`
# Issues
Please report bugs and request enhancements using the [issue tracker](https://github.com/jdidion/xphyle).
# Roadmap
Future releases are mapped out using [GitHub Projects](https://github.com/jdidion/xphyle/projects).
# Citing xphyle
[Didion, JP (2017) xphyle: Extraordinarily simple file handling. Journal of Open Source Software; doi:10.21105/joss.00255](https://joss.theoj.org/papers/10.21105/joss.00255#)
# Acknowledgements
* [Dependencies scanned by PyUp.io](http://pyup.io/)
* Thanks to [@ctb](https://github.com/ctb) for reviewing the xphyle paper xphyle-4.4.4/docs/ 0000775 0000000 0000000 00000000000 14512304031 0013755 5 ustar 00root root 0000000 0000000 xphyle-4.4.4/docs/Makefile 0000664 0000000 0000000 00000015437 14512304031 0015427 0 ustar 00root root 0000000 0000000 # Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXAPI = sphinx-apidoc
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
MOSTSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS)
ALLSPHINXOPTS = $(MOSTSPHINXOPTS) .
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
all: html
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
api:
$(SPHINXAPI) -f -o api .. ../setup.py ../tests/*
html:
$(SPHINXBUILD) -b html $(MOSTSPHINXOPTS) api $(BUILDDIR)/html
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/atropos.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/atropos.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/atropos"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/atropos"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
xphyle-4.4.4/docs/api/ 0000775 0000000 0000000 00000000000 14512304031 0014526 5 ustar 00root root 0000000 0000000 xphyle-4.4.4/docs/api/conf.py 0000664 0000000 0000000 00000020627 14512304031 0016034 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
#
# xphyle API documentation build configuration file, created by
# sphinx-quickstart on Fri Sep 12 09:11:16 2014.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath(os.pardir))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
# Add autodoc and napoleon to the extensions list
extensions = ["sphinx.ext.autodoc", "sphinxcontrib.napoleon"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = "modules"
# General information about the project.
project = u"xphyle"
copyright = u"Public domain (government work), by John P Didion"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
from xphyle import __version__
#
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = __version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ["tests", "setup.py", "build", "dist", "_build"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
# keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = "default"
try:
from better import better_theme_path
html_theme_path = [better_theme_path]
html_theme = "better"
except ImportError:
pass
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
# html_logo = 'logo.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
# html_additional_pages = {}
# If false, no module index is generated.
# html_domain_indices = True
# If false, no index is generated.
# html_use_index = True
# If true, the index is split into individual pages for each letter.
# html_split_index = False
# If true, links to the reST sources are added to the pages.
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = "xphyledoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
"papersize": "a4paper",
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
("index", "xphyle.tex", u"xphyle Documentation", u"John P Didion", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False
# If true, show page references after internal links.
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
# latex_appendices = []
# If false, no module index is generated.
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [("index", "xphyle", u"xphyle Documentation", [u"John P Didion"], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
"index",
"xphyle",
u"xphyle Documentation",
u"John P Didion",
"xphyle",
"Transparently open compressed files",
"io",
),
]
# Documents to append as an appendix to all manuals.
# texinfo_appendices = []
# If false, no module index is generated.
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
# texinfo_no_detailmenu = False
xphyle-4.4.4/docs/api/modules.rst 0000664 0000000 0000000 00000001630 14512304031 0016730 0 ustar 00root root 0000000 0000000 xphyle package
==============
Public API
----------
xphyle module
~~~~~~~~~~~~~
.. automodule:: xphyle
:members:
:undoc-members:
:show-inheritance:
xphyle.utils module
~~~~~~~~~~~~~~~~~~~
.. automodule:: xphyle.utils
:members:
:undoc-members:
:show-inheritance:
xphyle.paths module
~~~~~~~~~~~~~~~~~~~
.. automodule:: xphyle.paths
:members:
:undoc-members:
:show-inheritance:
Plugin API
----------
You shouldn't need these modules unless you want to extend xphyle functionality.
xphyle.formats module
~~~~~~~~~~~~~~~~~~~~~
.. automodule:: xphyle.formats
:members:
:undoc-members:
:show-inheritance:
xphyle.progress module
~~~~~~~~~~~~~~~~~~~~~~
.. automodule:: xphyle.progress
:members:
:undoc-members:
:show-inheritance:
xphyle.urls module
~~~~~~~~~~~~~~~~~~
.. automodule:: xphyle.urls
:members:
:undoc-members:
:show-inheritance:
xphyle-4.4.4/docs/conf.py 0000664 0000000 0000000 00000020644 14512304031 0015262 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# xphyle documentation build configuration file, created by
# sphinx-quickstart on Fri Sep 12 09:11:16 2014.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath(os.pardir))
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
from xphyle import __version__
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = __version__
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
# Add autodoc and napoleon to the extensions list
extensions = ["sphinx.ext.autodoc", "sphinx.ext.napoleon"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# The suffix of source filenames.
source_suffix = ".rst"
# The encoding of source files.
# source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = "index"
# General information about the project.
project = u"xphyle"
copyright_ = u"Public domain (government work), by John P Didion"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
# language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
# today = ''
# Else, today_fmt is used as the format for a strftime call.
# today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ["tests", "setup.py", "build", "dist", "_build"]
# The reST default role (used for this markup: `text`) to use for all
# documents.
# default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
# add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
# add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
# show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting.
# modindex_common_prefix = []
# If true, keep warnings as "system message" paragraphs in the built documents.
# keep_warnings = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = "default"
try:
from better import better_theme_path
html_theme_path = [better_theme_path]
html_theme = "better"
except ImportError:
pass
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
# html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
# html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
# html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
# html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
# html_logo = 'logo.png'
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
# html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation.
# html_extra_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
# html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
# html_additional_pages = {}
# If false, no module index is generated.
# html_domain_indices = True
# If false, no index is generated.
# html_use_index = True
# If true, the index is split into individual pages for each letter.
# html_split_index = False
# If true, links to the reST sources are added to the pages.
# html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
# html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
# html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
# html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
# html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = "xphyledoc"
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
"papersize": "a4paper",
# The font size ('10pt', '11pt' or '12pt').
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
# 'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
("index", "xphyle.tex", u"xphyle Documentation", u"John P Didion", "manual"),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
# latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
# latex_use_parts = False
# If true, show page references after internal links.
# latex_show_pagerefs = False
# If true, show URL addresses after external links.
# latex_show_urls = False
# Documents to append as an appendix to all manuals.
# latex_appendices = []
# If false, no module index is generated.
# latex_domain_indices = True
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [("index", "xphyle", u"xphyle Documentation", [u"John P Didion"], 1)]
# If true, show URL addresses after external links.
# man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
"index",
"xphyle",
u"xphyle Documentation",
u"John P Didion",
"xphyle",
"Transparently open compressed files",
"io",
),
]
# Documents to append as an appendix to all manuals.
# texinfo_appendices = []
# If false, no module index is generated.
# texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
# texinfo_show_urls = 'footnote'
# If true, do not generate a @detailmenu in the "Top" node's menu.
# texinfo_no_detailmenu = False
xphyle-4.4.4/docs/developers.rst 0000664 0000000 0000000 00000001444 14512304031 0016662 0 ustar 00root root 0000000 0000000 Style-wise, we try to adhere to the [Google python style guidelines](https://google.github.io/styleguide/pyguide.html).
We use Google-style docstrings, which are formatted by the [Napoleon Sphinx Plugin](https://pypi.python.org/pypi/sphinxcontrib-napoleon).
We run pylint as part of each build and strive to maintain a 10/10 score. However, we disable some pylint checks:
* Function annotations: pylint does not properly handle whitespace around function annotations (https://github.com/PyCQA/pylint/issues/238).
* White space on empty lines: we use white space as a visual guide to the structure of the code. Each blank line should have whitespace matching the indent level of the next non-blank line.
* Checks that are arbitrary/overly restrictive (e.g. 'too-many-xxx'; see .pylintrc for full list)
xphyle-4.4.4/docs/index.rst 0000664 0000000 0000000 00000055464 14512304031 0015634 0 ustar 00root root 0000000 0000000 xphyle: extraordinarily simple file handling
============================================
.. image:: logo.png
:height: 200px
:width: 200 px
xphyle is a small python (3.4+) library that makes it easy to open compressed
files and URLs for the highest possible performance available on your system.
* `API `_
* `Source code `_
* `Report an issue `_
Installation
------------
xphyle is available from pypi::
pip install xphyle
xphyle tries to use the compression programs installed on your local machine (e.g. gzip, bzip2); if it can't, it will use the built-in python libraries (which are slower). Thus, xphyle has no required dependencies, but we recommend that if you install gzip, etc. if you don't already have them.
xphyle will use alternative programs for multi-threaded compression if it is available:
* gzip: `igzip `_ or `pigz `_.
* bzip2: `pbzip2 `_
Multithreading support is disabled by default; to set the number of threads that xphyle should use::
xphyle.configure(threads=4)
or, to automatically set it to the number of cores available on your system::
xphyle.configure(threads=True)
If you have programs installed at a location that is not on your path, you can add those locations to xphyle's executable search::
xphyle.configure(executable_path=['/path', '/another/path', ...])
If you would like progress bars displayed for file operations, you need to configure one or both of the python-level and system-level progress bars.
For python-level operations, the `pokrok `_ API is used by default. Pokrok provides access to many popular progress bar libraries with a single, standard interface. Please see the documentation for more information about which libraries are currently supported and how to configure them. To enable this::
> pip install pokrok
xphyle.configure(progress=True)
You can also use you own preferred progress bar by passing a callable, which must take a single iterable argument and two optional keyword arguments and return an iterable::
def my_progress_wrapper(itr, desc='My progress bar', size=None):
...
xphyle.configure(progress=my_progress_wrapper)
For system-level operations, an executable is required that reads from stdin and writes to stdout; `pv `_ is used by default. To enable this::
xphyle.configure(system_progress=True)
You can also use your own preferred program by passing a tuple with the command and arguments (:py:func:`` simplifies this)::
xphyle.configure(system_progress=xphyle.progress.system_progress_command(
'pv', '-pre', require=True))
Working with files
------------------
The heart of xphyle is the simplicity of working with files. There is a single interface -- ``xopen`` -- for opening "file-like objects", regardless of whether they represent local files, remote files (referenced by URLs), or system streams (stdin, stdout, stderr); and regardless of whether they are compressed.
The following are functionally equivalent ways to open a gzip file::
import gzip
f = gzip.open('input.gz', 'rt')
from xphyle import xopen
f = xopen('input.gz', 'rt')
So then why use xphyle? Two reasons:
1. The ``gzip.open`` method of opening a gzip file above requires you to know that you are expecting a gzip file and only a gzip file. If your program optionally accepts either a compressed or a decompressed file, then you'll need several extra lines of code to either detect the file format or to make the user specify the format of the file they are providing. This becomes increasingly cumbersome with each additional format you want to support. On the other hand, ``xopen`` has the same interface regardless of the compression format. Furthermore, if xphyle doesn't currently support a file format that you would like to use, it enables you to add it via a simple API.
2. The ``gzip.open`` method of opening a gzip file uses python code to decompress the file. It's well written, highly optimized python code, but unfortunately it's still slower than your natively compiled system-level applications (e.g. pigz or gzip). The ``xopen`` method of opening a gzip file first tries to use pigz or gzip to decompress the file and provides access to the resulting stream of decompressed data (as a file-like object), and only falls back to ``gzip.open`` if neither program is available.
If you want to be explicit about whether to expect a compressed file, what type of compression to expect, or whether to try and use system programs, you can::
from xphyle import xopen
from xphyle.paths import STDIN
# Expect the file to not be compressed
f = xopen('input', 'rb', compression=False)
# Open a remote file. Expect the file to be compressed, and throw an error
# if it's not, or if the compression format cannot be determined.
f = xopen('http://foo.com/input.gz', 'rt', compression=True)
# Open stdin. Expect the input to be gzip compressed, and throw an error if
# it's not
f = xopen(STDIN, 'rt', compression='gzip')
# Do not try to use the system-level gzip program for decompression
f = xopen('input.gz', 'rt', compression='gzip', use_system=False)
By default, ``xopen`` returns the file. If desired, ``xopen`` can also wrap the file such that it behaves just like a file with a few additional features:
* A file iterator is wrapped in a progress bar (if they have been enabled via the ``configure`` method described above).
* A simple event system that enables callbacks to be registered for various events. Currently, the only supported event is closing the file. The ``xphyle.utils`` package provides a few useful event listeners, e.g. to compress, move, or delete the file when it is closed.
* ContextManager functionality, such that the file is always compatible with ``with``, e.g.::
def print_lines(path):
# this works whether path refers to a local file, URL or STDIN
with xopen(path, context_wrapper=True) as infile:
for line in infile:
print(line)
The wrapping behavior can be enabled by passing ``context_wrapper=True`` to ``xopen``. You can configure ``xopen`` to wrap files by default::
xphyle.configure(default_xopen_context_wrapper=True)
**Note that this represents a change from xphyle 1.x, in which wrapping occurred by default.**
Another common pattern is to write functions that accept either a path or an open file object. Rather than having to test whether the user passed a path or a file and handle each differently, you can use the ``open_`` convenience method::
from xphyle import open_
def print_lines(path_or_file):
with open_(path_or_file) as infile:
for line in infile:
print(line)
Note that ``open_`` wraps files by default, including already open file-like objects. To disable this, set ``wrap_fileobj=False``.
Supported file formats
~~~~~~~~~~~~~~~~~~~~~~
xphyle supports the most commonly used file formats: gzip, bzip2/7zip, and lzma/xz.
Also supported are:
* zstandard
* Brotli
* block-based gzip (bgzip), a format commonly used in bioinformatics. Somewhat confusingly, '.gz' is an acceptable extension for bgzip files, and gzip will decompress bgzip files. Thus, to specifically use bgzip, either use a '.bgz' file extension or specify 'bgzip' as the compression format::
f = xopen('input.gz', 'rt', compression='bgzip', validate=False)
Additional compression formats may be added in the future. To get the most up-to-date list::
from xphyle.formats import FORMATS
print(', '.join(FORMATS.list_compression_formats())
When a file is opened for decompression, its extension is used to determine which decompressor to use. If the extension is not recognized, or if the filename is not available (e.g. when decompressing a stream or buffer), then xphyle attempts to determine the file format from the "magic bytes" at the beginning of the file.
Processes
~~~~~~~~~
As of xphyle 2.0.0, you can easily open subprocesses using the ``xphyle.popen`` method. This method is similar to python ``subprocess.Popen``, except that it uses ``xopen`` to open files passed to stdin, stdout, and stderr, and/or to wrap subprocess PIPEs. ``xphyle.popen`` returns an ``xphyle.Process`` object, which is a subclass of ``subprocess.Popen`` but adds additional functionality, essentially making a Process behave like a regular file. Writing to a process writes to its stdin PIPE, and reading from a process reads from its stdout or stderr PIPE::
from xphyle import popen, PIPE
proc = popen('cat', stdin=PIPE, stdout='myfile.gz')
try:
proc.write('foo')
finally:
proc.close()
# equivalent to:
with popen('cat', stdin=PIPE, stdout='myfile.gz') as proc:
proc.write('foo')
# and also to:
popen('cat', stdin=PIPE, stdout='myfile.gz').communicate('foo')
# for the common case above, there's also a shortcut method
from xphyle.utils import exec_process
exec_process('cat', 'foo', stdout='myfile.gz')
In addition, ``open_`` and ``xopen`` can open subprocesses. The primary difference is that ``popen`` enables customization of stdin, stdout, and stderr, whereas opening a process through ``open_`` or ``xopen`` uses default behavior of opening PIPEs for all of the streams, and wrapping the PIPE indicated by the file mode. For example::
# write to the process stdin
with open_('|cat', 'wt') as proc:
proc.write('foo')
# this command wraps stdin with gzip compression
with open_('|zcat', 'wt', compression='gzip') as proc:
proc.write('foo')
# this command wraps stdout with gzip decompression;
# furthermore, the compression format is determined
# automatically
with open_('|gzip -c foobar.txt', 'rt') as proc:
text = proc.read()
Note that with ``open_`` and ``xopen``, the system command must be specified as a string starting with '|'.
Buffers
~~~~~~~
As of xphyle 2.1.0, ``open_`` and ``xopen`` can also open buffer types. A buffer is an instance of ``io.StringIO`` or ``io.BytesIO`` (or similar) -- basically an in memory read/write buffer. Passing open buffer objects worked before (they were treated as file-like), but now there is a special file type -- ``FileType.BUFFER`` -- that will cause them to be handled a bit differently. In addition, you can now pass ``str`` or ``bytes`` (the type objects) to automatically create the corresponding buffer type::
with open_(str) as buf:
buf.write('foo')
string_foo = buf.getvalue()
# with compression, type must be 'bytes'
with open_(bytes, compression='gzip') as buf:
buf.write('foo')
compressed_foo = buf.getvalue()
You can also create readable buffers by passing the string/bytes to read instead of a path, and explicitly specifying the file type::
with open_("This is a string I want to read", file_type=FileType.BUFFER) as buf:
buf_str = buf.read()
Reading/writing data
~~~~~~~~~~~~~~~~~~~~
The ``xphyle.utils`` module provides methods for many of the common operations that you'll want to perform on files. A few examples are shown below; you can read the `API docs `_ for a full list of methods and more detailed descriptions of each.
There are pairs of methods for reading/writing text and binary data using iterators::
# Copy from one file to another, changing the line separator from
# unix to windows
from xphyle.utils import read_lines, write_lines
write_lines(
read_lines('linux_file.txt')
'windows_file.txt',
linesep='\r')
# Copy from one binary file to another, changing the encoding from
# ascii to utf-8
from xphyle.utils import read_bytes, write_bytes
def ascii2utf8(x):
if isinstance(x, bytes):
x = x.decode('ascii')
return x.encode('utf-8')
write_bytes(
read_bytes('ascii_file.txt', convert=ascii2utf8),
'utf8-file.txt')
There's another pair of methods for reading/writing key=value files::
from collections import OrderedDict
from xphyle.utils import read_dict, write_dict
cats = OrderedDict((fluffy,'calico'), (droopy,'tabby'), (sneezy,'siamese'))
write_dict(cats, 'cats.txt.gz')
# change from '=' to '\t' delimited; preserve the order of the items
write_dict(
read_dict(cats, 'cats.txt.gz', ordered=True),
'cats.tsv', sep='\t')
You can also read from delimited files such as csv and tsv::
from xphyle.utils import read_delimited, read_delimited_as_dict
class Dog(object):
def __init__(self, name, age, breed):
self.name = name
self.age = age
self.breed = breed
def pet(self): ...
def say(self, message): ...
for dog in read_delimited(
'dogs.txt.gz', header=True,
converters=(str,int,str),
row_type=Dog):
dog.pet()
dogs = read_delimited_as_dict(
'dogs.txt.gz', header=True,
key='name', converters=(str,int,str),
row_type=Dog):
dogs['Barney'].say('Good Boy!')
There are convenience methods for compressing and decompressing files::
from xphyle.utils import compress_file, decompress_file, transcode_file
# Gzip compress recipes.txt, and delete the original
compress_file('recipes.txt', compression='gzip', keep=False)
# decompress a remote compressed file to a local file
decompress_file('http://recipes.com/allrecipes.txt.gz',
'local_recipes.txt')
# Change from gzip to bz2 compression:
transcode_file('http://recipes.com/allrecipes.txt.gz',
'local_recipes.txt.bz2')
There is a replacement for ``fileinput``::
from xphyle.utils import fileinput
# By default, read from the files specified as command line arguments,
# or stdin if there are no command line arguments, and autodetect
# the compression format
for line in fileinput():
print(line)
# Read from multiple files as if they were one
for line in fileinput(('myfile.txt', 'myotherfile.txt.gz')):
print(line)
There's also a set of classes for writing to multiple files::
from xphyle.utils import fileoutput
from xphyle.utils import TeeFileOutput, CycleFileOutput, NCycleFileOutput
# write all lines in sourcefile.txt to both file1 and file2.gz
with fileoutput(
('file1', 'file2.gz'),
file_output_type=TeeFileOutput) as out:
out.writelines(read_lines('sourcefile.txt'))
# Alternate writing each line in sourcefile.txt to file1 and file2.gz
with fileoutput(
('file1', 'file2.gz'),
file_output_type=CycleFileOutput) as out:
out.writelines(read_lines('sourcefile.txt'))
# Alternate writing four lines in sourcefile.txt to file1 and file2.gz
with fileoutput(
('file1', 'file2.gz'),
file_output_type=NCycleFileOutput, n=4) as out:
out.writelines(read_lines('sourcefile.txt'))
# Write up to 10,000 lines in each file before opening the next file
with RollingFileOutput('file{}.gz', n=10000) as out:
out.writelines(read_lines('sourcefile.txt'))
And finally, there's some miscellaneous methods such as linecount::
from xphyle.utils import linecount
print("There are {} lines in file {}".format(
linecount(path), path))
File paths
~~~~~~~~~~
The ``xphyle.paths`` module provides methods for working with file paths. The `API docs `_ have a full list of methods and more detailed descriptions of each. Here are a few examples::
from xphyle.paths import *
# Get the absolute path, being smart about STDIN/STDOUT/STDERR and
# home directory shortcuts
abspath('/foo/bar/baz') # -> /foo/bar/baz
abspath('foo') # -> /path/to/current/dir/foo
abspath('~/foo') # -> /home/myname/foo
abspath(STDIN) # -> STDIN
# Splat a path into its component parts
dir, name, *extensions = split_path('/home/joe/foo.txt.gz') # ->
# dir = '/home/joe'
# name = 'foo'
# extensions = ['txt', 'gz']
# Check that a path exists, is a file, and allows reading
# Raises IOError if any of the expectations are violated,
# otherwise returns the fully resolved path
path = check_path('file.txt.gz', 'f', 'r')
# Shortcuts to check whether a file is readable/writeable
path = check_readable_file('file.txt')
path = check_writeable_file('file.txt')
# There are also 'safe' versions of the methods that return
# None rather than raise IOError
path = safe_check_readable_file('nonexistant_file.txt') # path = None
# Find all files in a directory (recursively) that match a
# regular expression pattern
find('mydir', 'file.*\.txt\.gz')
# Lookup the path to an executable
gzip_path = get_executable_path('gzip')
`TempDir `_ is a particularly useful class, especially for unit testing. In fact, it us used extensively for unit testing xphyle itself. TempDir can be thought of as a virtual file system. It creates a temporary directory, and it provides methods to create subdirectories and files within that directory. When the ``close()`` method is called, the entire temporary directory is deleted. ``TempDir`` can also be used as a ContextManager::
with TempDir() as temp:
# create three randomly named files under 'tempdir'
paths = temp.make_empty_files(3)
# create directory 'tempdir/foo'
foo = temp.make_directory('foo')
# create a randomly named file with the '.gz' suffix
# within directory 'tempdir/foo'
gzfile = temp[foo].make_file(suffix='.gz')
Another useful set of classes is `FileSpec `_, `DirSpec `_, and `PathSpec `_. These classes help with the common problem of working files that match a specific pattern, especially when you need to then extract some pieces of information from the file names. For example, you may need to find all the files starting with 'foo' within any subdirectory of '/bar', and then performing different operations depending on the extension. You could use a PathSpec for this::
spec = PathSpec(
DirSpec(PathVar('subdir'), template=os.path.join('/bar', '{subdir}')),
FileSpec(
PathVar('name', pattern='foo.*'),
PathVar('ext'),
template='{name}.{ext}'))
files = spec.find(recursive=True)
for f in files:
if f['ext'] == 'txt':
process_text_file(f)
else:
process_binary_file(f)
A FileSpec or DirSpec has two related fields: a template, which is a python `fstring `_ and is used for constructing filenames from component pieces; and a pattern, which is a regular expression and is used for matching to path strings. The named components of the template correspond to path variables (instances of the `PathVar `_ class). Each PathVar can provide its own pattern, as well as lists of valid or invalid values. If a pattern is not specified during FileSpec/DirSpec creation, the pattern is automatically created by simply substituting the PathVar patterns for the corresponding components in the template string ('.*' by default).
Note that a DirSpec is only able to construct/match directory paths, and a FileSpec is only able to construct/match file names. A PathSpec is simply a composite type of a DirSpec and a FileSpec that can be used to construct/match full paths.
Each of the *Spec classes has three methods:
* construct: Given values for all of the path vars, construct a new path. Note that __call__ is an alias for construct.
* parse: Match a path against the *Spec's pattern. If the path matches, the component's are extracted (through the use of named capture groups), otherwise an exception is raised.
* find: Find all directories/files/paths that match the *Spec's pattern.
All of these methods return a PathInst, which is a subclass of pathlib.Path (specifically, a subclass of pathlib.WindowsPath when code is run on Windows, otherwise a PosixPath) that has an additional slot, 'values', that is a dictionary of the component name, value pairs, and overrides a few methods.
Extending xphyle
----------------
You can add support for another compression format by extending one of the base classes in :py:mod:``::
import xphyle.formats
class FooFormat(xphyle.formats.SingleExeCompressionFormat):
"""Implementation of CompressionFormat for foo files.
"""
@property
def name(self) -> str:
return 'foo'
@property
def exts(self) -> Tuple[str, ...]:
return ('foo',)
@property
def system_commands(self) -> Tuple[str, ...]:
return ('foo',)
@property
def compresslevel_range(self) -> Tuple[int, int]:
# because of course it goes to 11
return (1, 11)
@property
def default_compresslevel(self) -> int:
return 6
@property
def magic_bytes(self) -> Tuple[Tuple[int, ...], ...]:
return ((0x0F, 0x00),)
@property
def mime_types(self) -> Tuple[str, ...]:
return ('application/foo',)
# build the system command
# op = 'c' for compress, 'd' for decompress
# src = the source file, or STDIN if input should be read from stdin
# stdout = True if output should be written to stdout
# compresslevel = the compression level
def get_command(self, op, src=STDIN, stdout=True, compresslevel=6):
cmd = [self.executable_path]
if op == 'c':
# adjust the compresslevel to be within the range allowed
# by the program
compresslevel = self._get_compresslevel(compresslevel)
cmd.append('-{}'.format(compresslevel))
cmd.append('-z')
elif op == 'd':
cmd.append('-d')
if stdout:
cmd.append('-c')
if src != STDIN:
cmd.append(src)
return cmd
def open_file_python(self, filename, mode, **kwargs):
# self.lib is a property that lazily imports and returns the
# python library named in the ``name`` member above
return self.lib.open_foo(filename, mode, **kwargs)
Then, register your format::
xphyle.formats.register_compression_format(FooFormat)
Also, note that you can support custom URL schemes by the standard method of adding `urllib `_ handlers::
import urllib.request
urllib.request.OpenerDirector.add_handler(my_handler)
xphyle-4.4.4/docs/logo.pdf 0000664 0000000 0000000 00002261211 14512304031 0015415 0 ustar 00root root 0000000 0000000 %PDF-1.5
%âãÏÓ
1 0 obj
<>/OCGs[5 0 R 82 0 R]>>/Pages 3 0 R/Type/Catalog>>
endobj
2 0 obj
<>stream
Adobe Illustrator CC 2014 (Macintosh)
2016-11-05T07:22:50-04:00
2016-11-06T20:06:21-05:00
2016-11-06T20:06:21-05:00
232
256
JPEG
/9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA
AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK
DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f
Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAADoAwER
AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA
AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB
UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE
1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ
qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy
obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp
0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo
+DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7
FXYq7FXVGKrZJYo0LyOqIoqzMQAB7k4q8/8AN/51+TPL0IaO6j1GVq8RBKnpAr1DTGqDr2qcICvK
td/5y59MtHp2nRB1OxV2nHT+YrCv4ZLhZ8LE3/5yw87swYBFHYCFKfdy3x4U8LJ/L/8AzlpduiJq
NpBLKaAl+VqCf9dRMo+nHhRwvUfJ357+U/MCsl4RpMqlR6k0qPbMT/LcrRP+CpkGFPRoJ4Zollhk
WWKQVR0YMpHiCOuKquKuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVxOKoHWdY0
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
XYq7FXYq7FXYq7FXYq7FXYq7FX//2Q==
application/pdf
uuid:d58aeb55-496d-9942-852a-ffca05944809
xmp.did:f508bc93-1284-455b-9cbc-e280ab279b13
xmp.did:58c3cd87-9905-4b03-a5c9-d2f610a4a2b2
proof:pdf
created
xmp.iid:58c3cd87-9905-4b03-a5c9-d2f610a4a2b2
2016-11-05T07:04:08-04:00
Adobe Photoshop CC 2014 (Macintosh)
saved
xmp.iid:f508bc93-1284-455b-9cbc-e280ab279b13
2016-11-05T07:22:50-04:00
Adobe Illustrator CC 2014 (Macintosh)
/
uuid:c677e5cf-0d10-db45-a269-0e0e34a61379
xmp.did:58c3cd87-9905-4b03-a5c9-d2f610a4a2b2
xmp.did:58c3cd87-9905-4b03-a5c9-d2f610a4a2b2
EmbedByReference
/private/var/folders/8f/2xw3v3nn1wz3jn_mhvp_98811rbqhg/T/TemporaryItems/(A Document Being Saved By Illustrator 6)/HHtl2Q.tif
/private/var/folders/8f/2xw3v3nn1wz3jn_mhvp_98811rbqhg/T/TemporaryItems/(A Document Being Saved By Illustrator 6)/HHtl2Q.tif
3
Display
Adobe Photoshop for Macintosh -- Image Conversion Plug-in
1
True
False
612.000000
563.079346
Points
Cyan
Magenta
Yellow
Black
Default Swatch Group
0
endstream
endobj
3 0 obj
<>
endobj
7 0 obj
<>/Resources<>/ProcSet[/PDF/ImageC]/Properties<>/XObject<>>>/Thumb 93 0 R/TrimBox[0.0 0.0 612.0 563.079]/Type/Page>>
endobj
84 0 obj
<>stream
H‰Ò÷wVÐ÷u6PprqVà*äÒw6PH/æ234R0 BcS id`©g`nilbj®œË¥ï™k à’ÏÔ` `jf’T iÑ…qŠR¹Âò ‚ÔwCèŠAÅ!b®¾@Û¹ p¿
endstream
endobj
85 0 obj
<>
endobj
93 0 obj
<>stream
8;Y8d_.&lf%(6,35)IIsA6bl,b;cJfFGKfa&"ju\LP5%[niN%G'1eRpM9Rrh-@kXH
8U;0?l1?WgC.-FoV+?3sU]p!?;@sPPi+hH9#S7tB4qs*2MF`b6NW]isJ<`kW5]K\q
68f[c7Z*uB3.V8&i&K%W#%[kSb/j1TZ4&a$//X[=O0?\2k\&+O_siJSKZ)7`G@%Rg
r8cHs)B_u5Vh."j9_Kh3QQ4ncmL/VNq/ln948XeKi>n]*lp`HARiiSq]K*Q,>)XXq
QNNN64QfEA6r?U+k.].'3_.GF'WZJ-tF#bAknM)aATr\eqLM?t:Q2+i.j`'8&_p
l)Ar7E*HuFC@jYc7IO(Ye%B_f*p_E=HIc_Il3cVnq
G>E`bdt62Fl&D&[HoabV7h?JHqD!j$eP1"hPtAD<@.bT5Y\Fbo^sXU%q>p[(cBniElT2S$:@esa8&!:0Y+EB"Gr+KQ&)D2'Yb
/'T%Ef'&qNEL.'!YhI0VWKj"];@1o\347f*5ce'pG$.pGUcW-FnY/,nkK7aC38sOY
K$(l4DG.Qif!bC]otTng!5*mY/c~>
endstream
endobj
94 0 obj
[/Indexed/DeviceRGB 255 95 0 R]
endobj
95 0 obj
<>stream
8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0
b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup`
E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn
6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O(
l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~>
endstream
endobj
90 0 obj
<>/ExtGState<>/ProcSet[/PDF/ImageC/ImageI]/XObject<>>>/Subtype/Form>>stream
q
/GS0 gs
302 0 0 342 153 -14.9206543 cm
/Im0 Do
Q
endstream
endobj
91 0 obj
<>/ProcSet[/PDF/ImageC]/XObject<>>>/Subtype/Form>>stream
q
/GS0 gs
135.9999936 0 0 136.5938801 251 30.6732403 cm
/Im0 Do
Q
endstream
endobj
92 0 obj
<>/Filter/FlateDecode/Height 354/Intent/RelativeColorimetric/Length 174382/Name/X/Subtype/Image/Type/XObject/Width 612>>stream
H‰ì—‰SYÇÿ–=ªv×u</„œ@ÈÑ I@!Ü
*Sã¹³#Ž3ºj9;åÖ¨S¥îêx‚Œºãˆ r”$$éô•ýu´mã¸åîªõ>õ«W/¿tBúGÕûô7Ä`0¯†}nö
ñË|Êâ8šei†¡ `Ãqÿ3CÍÈšîKþƒ9À]‹;h¯û=ƒyÿ i°àJÔA‰+(¬ôKN¼™ª‚ÜëË…TICÁ^B“cØ ÚdÅ„šîK^4·%4¹ixƒÿƒÁ¼åPE‡@^úS»žÂŒÁH?¾ W²®|©èI]¾!W†M 6ðšì4¼©ùc0æÿÊDpÎ’$}!¼^/ìáü¼I!d+pbEæâ•ÇÒ*ÊþâåBø)Ò@/áztA˜ùwÙ *±=A‚aúãý2c˜¡I(ô.Ø3Ü“!”ï‡>ËR¼gáfá1 ì`ÊA1!`Vè±LñL„o@—Aç¿úïÃ`0Ìÿ ±‘.ä¤$D¤ d
¸½+dLd=T! FäD&HÐ%M2 PÔD©sR¯üìËQ”¡XФÁ°P¾qïø˜Çó|VØü$8iñ"§¸o"Õ‚1i¸Q? %ˆÝ/"òQÍ
_/ž‰ø#hžb{b0æÝE°€pìÃKØÀáÔ x7ò#/, oÒ“Å„j2ò@0HM6™ÐžºX’£½_>¾HOÀ3<>üôÙ@ïÓ¾wÏ£î®öήŽÎÞ®'C}ƒ£#¤ÇÏ’ÌÄgý¦^$Ó‰_?€â¸ „~g€å;è×rÁ—îåWiUôØ ž’àʰÑa0æ]G°¬è´G©aÚM()´Šõ2#ñ³,9±¡Æiï¨ì™gd`tÈ
ìëéÒÙÛý°§³½«£õaÛímwÚÛïv´ÝypûÚÝk—¯ÿt¡åRãÅÆ“çN?uæÄé¦Ó-盯6_¹wónGk{ÏÃn°'”»»÷é“~Ðèpÿ³çC£þç>Ê ‡rÈŒ QÁ¤Œè§¢úúS¤)¾ %MìJƒy_+€á÷û!fŠí ðneD¡R,ÊІñs`ƾîþGmA÷o·]¿rãòÅ«çN6=|¬á›{¾Ú»ý/_lùt릺Íu5ŸÔTÔ®©¨Y[½nýêºÚªµkÊkV—WW•VV——º\…%°–—Uº* Y]VU[U³qý†íŸÕ½{ß¡†ƒG9¦ LÚzëDÑ!÷ÀØÐsßs/„Ð`€³t0Ã
¹Ý82ݯɛS^€¾xÅ3ƒÁ`Þ)ÄA òaú»R°ÀDžb&¼)’)/íîéïyÚóè ÈñÊÅ«?=yøÀ`ÆïþÞ°{ÇžíŸ}f¬©¬-Î/±[œúCº:#I–,W$ÄHâ—'Hb¤Šx¥"A D©•R¢P$Èåñ2iœD›½bù¢èÅ-‹ZªL´úg>htë†-_nÛñí×ûýpôì‰3Í—®]þùöµ[]í½O=Ãcã#ÿ˜o"f2A†¢#S!Ü&7=½‡ùT˜•0¢WÚƒÁ`0o3‘'?Ê(9Âi/õb‡
‚¼ ~$=ÑÁ1wW_GëÃk—¯Ÿ9qöÈ¡£;·ïÚ¶¥~ÃúàÄJWuÙªòÒ¢2¨|Ga®-Ïnq؈«ÉfÖ[Œ:èVƒÖ›¬t=¬°7eD–Ù¬',z³Õ”m#¬°Zf"ËdÔÁe™)éj¥
94E©Öi´¦L#\ã´:Šò
K‹\%åKëÖ¬ßüɦ¯@ ûxž>~êÊ¥ËÚ¹Ÿ
ŽŒŒø|¾0c†Ý&ENñ”‚8Qb0Ì{‡X”È!?‚ÄS° Å'¯€Ÿ"}‘ÑÞ.÷ý›÷››ZŽ<¶³~׺ªu…¹«´j-DEI´$viœ,F–(MJ–$Ú¦J‡©MÕ
³V»Ùê„rZsórVæÛ VæäCÁ¦ÀQX”·jÕÊâ’—«°TH£ÐÕ:²P}vºŠ¥E•’D•BV¤À’ÄJc–Ä._³":^¶B4Çì€o¨ªù|˶C
›No¾ÐrçÎööv·Û=66·9Ÿ0KúEÀ]ü`Bˆç†Á`0˜÷ 8óáœ'IlQÁ™Ï¿§>Ëñ+ôyü}½ý]m®6ÿëÄ‘¿ÿæû½_îݺ~sµ«º4·$Ïšg7Ø‘¥ÒéRtFÞ˜a4g˜LZ‘n4¤!?ŠŠö!-†lHš°B¢„¬:Íúl":f¸Ø”i±è!]æØ‡ÝâtXraÍ! ¢:²¶l¤Ôl«Ñi5:,z»!Ü®ÊTÉ4Š„Dù
ejRZJ¢&]
eXþ
½º²fݺºÛuìŸÿhþ©¥µíþÀÐ ô“4ÅplèŽù¢Y†
rhÙFäõz|¾qx\ é tøq%áYì ™‰f¨&@>ût}ƒÁ¼
€QT„
êÀ¡
"€x:ö)Ô‡ßë9<~ð¨å|óñ#'5Þ½cÏÆºMyf§%ƒH“§&Æ*”1ò$°’T‘¥ädYóg±½¨<¿´²°¼¢ Ìå,†—VƒÕœe1dušLÈzšä´Ô$
T²\ÅGBeJ²L¥»Å+ “¤1JYl¢d¹B£Å%*âUIÒ•\“š¨MKÖe¦ ÕfræZ
®Uyee…«+Šk*Šk]U—È'2m†‹<.I«”ÆÉeqŠØ%+¢æ-þx…K”Ê$#aª¨ªüë¶Ï¿;ÐÐx¾énë½®žîáÑ&XJð&Å2H‹0p"