├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── arclib ├── __init__.py ├── _rfile.py ├── bz2.py ├── gz.py ├── lzma.py ├── tar.py └── zip.py ├── docs ├── Makefile ├── make.bat └── source │ ├── conf.py │ └── index.rst ├── setup.py └── tests ├── __pycache__ ├── test_bz2.cpython-27-PYTEST.pyc ├── test_bz2.cpython-34-PYTEST.pyc ├── test_gz.cpython-27-PYTEST.pyc ├── test_gz.cpython-34-PYTEST.pyc ├── test_lzma.cpython-27-PYTEST.pyc ├── test_lzma.cpython-34-PYTEST.pyc └── util.cpython-34.pyc ├── test_bz2.py ├── test_lzma.py ├── util.py └── util.pyc /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | docs/build/ 3 | dist/ 4 | MANIFEST 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Ryan Gonzalez 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | recursive-include docs source/* 3 | prune docs/build 4 | recursive-include tests *.py 5 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | arclib 2 | ====== 3 | 4 | arclib aims at providing a unified API for accessing Python's archive formats. In 5 | particular, it provides the following APIs: 6 | 7 | - Basic, one-shot: For gzip, bzip2, and LZMA. Supports one-shot compression and 8 | decompression. 9 | - Basic, incremental: For bzip2 and LZMA. Supports incremental compression and 10 | decompression. 11 | - Complex: For zip and tar. Supports accessing the various members and their 12 | respective information. 13 | 14 | Links 15 | ***** 16 | 17 | - `PyPI `_ 18 | - `Docs `_ 19 | -------------------------------------------------------------------------------- /arclib/__init__.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta as _ABCMeta, abstractmethod as _abstractmethod 2 | from functools import wraps as _wraps 3 | 4 | def _uabstractmethod(f): 5 | @_wraps(f) 6 | def func(*args): raise NotImplementedError() 7 | return _abstractmethod(func) 8 | 9 | class AbstractBasicCompressor(metaclass=_ABCMeta): 10 | @_uabstractmethod 11 | def compress(self, data): pass 12 | 13 | @_uabstractmethod 14 | def flush(): pass 15 | 16 | class AbstractBasicDecompressor(metaclass=_ABCMeta): 17 | @_uabstractmethod 18 | def decompress(self, data): pass 19 | 20 | @property 21 | @_uabstractmethod 22 | def eof(self): pass 23 | 24 | @property 25 | @_uabstractmethod 26 | def unused_data(self): pass 27 | 28 | class AbstractMemberInfo(metaclass=_ABCMeta): 29 | @property 30 | @_uabstractmethod 31 | def filename(self): pass 32 | 33 | @property 34 | @_uabstractmethod 35 | def size(self): pass 36 | 37 | @property 38 | @_uabstractmethod 39 | def mtime(self): pass 40 | 41 | # Yes, I am putting an implementation detail in an abstract class. No, I do 42 | # not care. 43 | def __str__(self): 44 | return '{}.{}({!r}, {}, {})'.format(self._module, self.__class__.__name__, 45 | self.filename, self.size, self.mtime) 46 | 47 | def __repr__(self): return str(self) 48 | 49 | class AbstractAdvancedFile(metaclass=_ABCMeta): 50 | @_uabstractmethod 51 | def close(self): pass 52 | 53 | @_uabstractmethod 54 | def info_for(self, member): pass 55 | 56 | @_uabstractmethod 57 | def all_info(self): pass 58 | 59 | @_uabstractmethod 60 | def members(self): pass 61 | 62 | @_uabstractmethod 63 | def dump(self): pass 64 | 65 | @_uabstractmethod 66 | def add(self, path, arcname=None, *, recursive=True): pass 67 | 68 | @_uabstractmethod 69 | def add_data(self, path, data): pass 70 | 71 | @_uabstractmethod 72 | def extract(self, path=None, **kw): pass 73 | 74 | @_uabstractmethod 75 | def extract_all(self, path=None, members=None, **kw): pass 76 | 77 | @_uabstractmethod 78 | def open(self, member, universal_newlines=False, **kw): pass 79 | -------------------------------------------------------------------------------- /arclib/_rfile.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | class RFile: 4 | def __init__(self, name, fp): 5 | self.__name = name 6 | self.__fp = fp 7 | 8 | @property 9 | def name(self): return self.__name 10 | 11 | def read(self, size=-1): 12 | return self.__fp.read(size) 13 | 14 | class RTextFile(io.TextIOWrapper): 15 | def __init__(self, name, *args, **kw): 16 | self.__name = name 17 | super(RTextFile, self).__init__(*args, **kw) 18 | 19 | @property 20 | def name(self): return self.__name 21 | -------------------------------------------------------------------------------- /arclib/bz2.py: -------------------------------------------------------------------------------- 1 | from . import AbstractBasicCompressor, AbstractBasicDecompressor 2 | from bz2 import BZ2Compressor as Compressor, BZ2Decompressor as Decompressor,\ 3 | BZ2File as File, open 4 | 5 | AbstractBasicCompressor.register(Compressor) 6 | AbstractBasicDecompressor.register(Decompressor) 7 | -------------------------------------------------------------------------------- /arclib/gz.py: -------------------------------------------------------------------------------- 1 | from gzip import compress, decompress, GzipFile as File, open 2 | -------------------------------------------------------------------------------- /arclib/lzma.py: -------------------------------------------------------------------------------- 1 | from . import AbstractBasicCompressor, AbstractBasicDecompressor 2 | from lzma import LZMACompressor as Compressor, LZMADecompressor as Decompressor,\ 3 | LZMAFile as File, open 4 | 5 | AbstractBasicCompressor.register(Compressor) 6 | AbstractBasicDecompressor.register(Decompressor) 7 | -------------------------------------------------------------------------------- /arclib/tar.py: -------------------------------------------------------------------------------- 1 | from . import AbstractAdvancedFile, AbstractMemberInfo 2 | from tarfile import is_tarfile as test, TarFile as _tfile, TarInfo as _info 3 | import datetime as _datetime, io as _io, time as _time 4 | from . import _rfile 5 | 6 | class Info(AbstractMemberInfo): 7 | _module = 'tar' 8 | 9 | def __init__(self, info, **kw): 10 | self.info = info 11 | 12 | @property 13 | def filename(self): return self.info.name 14 | @filename.setter 15 | def filename(self, value): 16 | self.info.name = value 17 | 18 | @property 19 | def size(self): return self.info.size 20 | @size.setter 21 | def size(self, value): 22 | self.info.size = value 23 | 24 | @property 25 | def mtime(self): return _datetime.datetime.fromtimestamp(self.info.mtime) 26 | @mtime.setter 27 | def mtime(self, value): 28 | self.info.mtime = value.timestamp() 29 | 30 | class File(AbstractAdvancedFile): 31 | def __init__(self, tar): 32 | self.__tar = tar 33 | 34 | def __enter__(self): 35 | return self 36 | 37 | def __exit__(self, *_): 38 | self.close() 39 | 40 | def close(self): 41 | self.__tar.close() 42 | 43 | def info_for(self, member): 44 | return Info(self.__tar.getmember(member)) 45 | 46 | def all_info(self): 47 | return list(map(Info, self.__tar.getmembers())) 48 | 49 | def members(self): 50 | return self.__tar.getnames() 51 | 52 | def dump(self): 53 | self.__tar.list() 54 | 55 | def add(self, path, arcname=None, *, recursive=True): 56 | self.__tar.add(path, arcname, recursive=recursive) 57 | 58 | def add_data(self, path, data): 59 | if isinstance(path, Info): 60 | info = path.info 61 | else: 62 | info = _info(path) 63 | info.size = len(data) 64 | info.mtime = _time.time() 65 | 66 | self.__tar.addfile(info, _io.BytesIO(data)) 67 | 68 | def extract(self, member, path=None): 69 | self.__tar.extract(member, '' if path is None else path) 70 | 71 | def extract_all(self, path=None, members=None): 72 | self.__tar.extractall('.' if path is None else path, members) 73 | 74 | def open(self, member, universal_newlines=False): 75 | try: 76 | fp = self.__tar.extractfile(member) 77 | except KeyError: 78 | return None 79 | 80 | if universal_newlines: 81 | return _rfile.RTextFile(member, fp) 82 | else: 83 | return _rfile.RFile(member, fp) 84 | 85 | def open(*args, **kw): 86 | return File(_tfile.open(*args, **kw)) 87 | 88 | def openobj(fileobj, **kw): 89 | return File(_tfile.open(name=kw.pop('name', None), fileobj=fileobj, **kw)) 90 | -------------------------------------------------------------------------------- /arclib/zip.py: -------------------------------------------------------------------------------- 1 | from . import AbstractAdvancedFile, AbstractMemberInfo 2 | from zipfile import is_zipfile as test, ZipFile as _zfile, ZipInfo as _info 3 | import os as _os, datetime as _datetime 4 | from . import _rfile 5 | 6 | class Info(AbstractMemberInfo): 7 | _module = 'zip' 8 | 9 | def __init__(self, info): 10 | self.info = info 11 | 12 | @property 13 | def filename(self): return self.info.filename 14 | @filename.setter 15 | def filename(self, value): 16 | self.info.filename = value 17 | 18 | @property 19 | def size(self): return self.info.file_size 20 | @size.setter 21 | def size(self, value): 22 | self.info.file_size = value 23 | 24 | @property 25 | def mtime(self): return _datetime.datetime(*self.info.date_time) 26 | @mtime.setter 27 | def mtime(self, value): 28 | self.info.date_time = tuple(value.timetuple()[:6]) 29 | 30 | class File(AbstractAdvancedFile): 31 | def __init__(self, zipfile): 32 | self.__zip = zipfile 33 | 34 | def __enter__(self): 35 | return self 36 | 37 | def __exit__(self, *_): 38 | self.close() 39 | 40 | def close(self): 41 | self.__zip.close() 42 | 43 | def info_for(self, member): 44 | return Info(self.__zip.getinfo(member)) 45 | 46 | def all_info(self): 47 | return list(map(Info, self.__zip.infolist())) 48 | 49 | def members(self): 50 | return self.__zip.namelist() 51 | 52 | def dump(self): 53 | self.__zip.printdir() 54 | 55 | def add(self, path, arcname=None, recursive=True): 56 | if _os.path.isdir(path) and recursive: 57 | for root, dirs, files in _os.walk(path): 58 | subroot = root[len(path):] 59 | 60 | for fpath in files: 61 | fullpath = _os.path.join(root, fpath) 62 | destpath = fullpath[len(path):].lstrip('/') 63 | 64 | if arcname: 65 | destpath = _os.path.join(arcname, destpath) 66 | 67 | self.__zip.write(fullpath, destpath) 68 | else: 69 | self.__zip.write(path, arcname) 70 | 71 | def add_data(self, path, data): 72 | self.__zip.writestr(path, data) 73 | 74 | def extract(self, member, path=None, *, pwd=None): 75 | self.__zip.extract(member, path, pwd) 76 | 77 | def extract_all(self, path=None, members=None, *, pwd=None): 78 | self.__zip.extractall(path, members, pwd=pwd) 79 | 80 | def open(self, member, universal_newlines=False, *, pwd=None): 81 | try: 82 | fp = self.__zip.open(member, pwd=pwd) 83 | except KeyError: 84 | return None 85 | 86 | if universal_newlines: 87 | return _rfile.RTextFile(member, fp) 88 | else: 89 | return _rfile.RFile(member, fp) 90 | 91 | def open(*args, **kw): 92 | return File(_zfile(*args, **kw)) 93 | 94 | def openobj(fileobj, **kw): 95 | return File(_zfile(fileobj, **kw)) 96 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(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/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | html: 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 61 | @echo 62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 63 | 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | pickle: 70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 71 | @echo 72 | @echo "Build finished; now you can process the pickle files." 73 | 74 | json: 75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 76 | @echo 77 | @echo "Build finished; now you can process the JSON files." 78 | 79 | htmlhelp: 80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 81 | @echo 82 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 83 | ".hhp project file in $(BUILDDIR)/htmlhelp." 84 | 85 | qthelp: 86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 87 | @echo 88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/arclib.qhcp" 91 | @echo "To view the help file:" 92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/arclib.qhc" 93 | 94 | applehelp: 95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 96 | @echo 97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 98 | @echo "N.B. You won't be able to view it unless you put it in" \ 99 | "~/Library/Documentation/Help or install it in your application" \ 100 | "bundle." 101 | 102 | devhelp: 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/arclib" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/arclib" 109 | @echo "# devhelp" 110 | 111 | epub: 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | latex: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo 119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 121 | "(use \`make latexpdf' here to do that automatically)." 122 | 123 | latexpdf: 124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 125 | @echo "Running LaTeX files through pdflatex..." 126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 128 | 129 | latexpdfja: 130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 131 | @echo "Running LaTeX files through platex and dvipdfmx..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | text: 136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 137 | @echo 138 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 139 | 140 | man: 141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 142 | @echo 143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 144 | 145 | texinfo: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo 148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 149 | @echo "Run \`make' in that directory to run these through makeinfo" \ 150 | "(use \`make info' here to do that automatically)." 151 | 152 | info: 153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 154 | @echo "Running Texinfo files through makeinfo..." 155 | make -C $(BUILDDIR)/texinfo info 156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 157 | 158 | gettext: 159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 160 | @echo 161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 162 | 163 | changes: 164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 165 | @echo 166 | @echo "The overview file is in $(BUILDDIR)/changes." 167 | 168 | linkcheck: 169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 170 | @echo 171 | @echo "Link check complete; look for any errors in the above output " \ 172 | "or in $(BUILDDIR)/linkcheck/output.txt." 173 | 174 | doctest: 175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 176 | @echo "Testing of doctests in the sources finished, look at the " \ 177 | "results in $(BUILDDIR)/doctest/output.txt." 178 | 179 | coverage: 180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 181 | @echo "Testing of coverage in the sources finished, look at the " \ 182 | "results in $(BUILDDIR)/coverage/python.txt." 183 | 184 | xml: 185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 186 | @echo 187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 188 | 189 | pseudoxml: 190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 191 | @echo 192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 193 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 2> nul 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\arclib.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\arclib.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # arclib documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Apr 18 14:25:46 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | import shlex 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | #sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | #needs_sphinx = '1.0' 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 31 | # ones. 32 | extensions = [] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # The suffix(es) of source filenames. 38 | # You can specify multiple suffix as a list of string: 39 | # source_suffix = ['.rst', '.md'] 40 | source_suffix = '.rst' 41 | 42 | # The encoding of source files. 43 | #source_encoding = 'utf-8-sig' 44 | 45 | # The master toctree document. 46 | master_doc = 'index' 47 | 48 | # General information about the project. 49 | project = u'arclib' 50 | copyright = u'2016, Ryan Gonzalez' 51 | author = u'Ryan Gonzalez' 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The short X.Y version. 58 | version = '0.1' 59 | # The full version, including alpha/beta/rc tags. 60 | release = '0.1' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | # 65 | # This is also used if you do content translation via gettext catalogs. 66 | # Usually you set "language" from the command line for these cases. 67 | language = None 68 | 69 | # There are two options for replacing |today|: either, you set today to some 70 | # non-false value, then it is used: 71 | #today = '' 72 | # Else, today_fmt is used as the format for a strftime call. 73 | #today_fmt = '%B %d, %Y' 74 | 75 | # List of patterns, relative to source directory, that match files and 76 | # directories to ignore when looking for source files. 77 | exclude_patterns = [] 78 | 79 | # The reST default role (used for this markup: `text`) to use for all 80 | # documents. 81 | #default_role = None 82 | 83 | # If true, '()' will be appended to :func: etc. cross-reference text. 84 | #add_function_parentheses = True 85 | 86 | # If true, the current module name will be prepended to all description 87 | # unit titles (such as .. function::). 88 | #add_module_names = True 89 | 90 | # If true, sectionauthor and moduleauthor directives will be shown in the 91 | # output. They are ignored by default. 92 | #show_authors = False 93 | 94 | # The name of the Pygments (syntax highlighting) style to use. 95 | pygments_style = 'sphinx' 96 | 97 | # A list of ignored prefixes for module index sorting. 98 | #modindex_common_prefix = [] 99 | 100 | # If true, keep warnings as "system message" paragraphs in the built documents. 101 | #keep_warnings = False 102 | 103 | # If true, `todo` and `todoList` produce output, else they produce nothing. 104 | todo_include_todos = False 105 | 106 | 107 | # -- Options for HTML output ---------------------------------------------- 108 | 109 | # The theme to use for HTML and HTML Help pages. See the documentation for 110 | # a list of builtin themes. 111 | try: 112 | import sphinx_rtd_theme 113 | except ImportError: 114 | html_theme = 'alabaster' 115 | else: 116 | html_theme = 'sphinx_rtd_theme' 117 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 118 | 119 | # Theme options are theme-specific and customize the look and feel of a theme 120 | # further. For a list of options available for each theme, see the 121 | # documentation. 122 | #html_theme_options = {} 123 | 124 | # Add any paths that contain custom themes here, relative to this directory. 125 | #html_theme_path = [] 126 | 127 | # The name for this set of Sphinx documents. If None, it defaults to 128 | # " v documentation". 129 | #html_title = None 130 | 131 | # A shorter title for the navigation bar. Default is the same as html_title. 132 | #html_short_title = None 133 | 134 | # The name of an image file (relative to this directory) to place at the top 135 | # of the sidebar. 136 | #html_logo = None 137 | 138 | # The name of an image file (within the static path) to use as favicon of the 139 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 140 | # pixels large. 141 | #html_favicon = None 142 | 143 | # Add any paths that contain custom static files (such as style sheets) here, 144 | # relative to this directory. They are copied after the builtin static files, 145 | # so a file named "default.css" will overwrite the builtin "default.css". 146 | html_static_path = ['_static'] 147 | 148 | # Add any extra paths that contain custom files (such as robots.txt or 149 | # .htaccess) here, relative to this directory. These files are copied 150 | # directly to the root of the documentation. 151 | #html_extra_path = [] 152 | 153 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 154 | # using the given strftime format. 155 | #html_last_updated_fmt = '%b %d, %Y' 156 | 157 | # If true, SmartyPants will be used to convert quotes and dashes to 158 | # typographically correct entities. 159 | #html_use_smartypants = True 160 | 161 | # Custom sidebar templates, maps document names to template names. 162 | #html_sidebars = {} 163 | 164 | # Additional templates that should be rendered to pages, maps page names to 165 | # template names. 166 | #html_additional_pages = {} 167 | 168 | # If false, no module index is generated. 169 | #html_domain_indices = True 170 | 171 | # If false, no index is generated. 172 | #html_use_index = True 173 | 174 | # If true, the index is split into individual pages for each letter. 175 | #html_split_index = False 176 | 177 | # If true, links to the reST sources are added to the pages. 178 | #html_show_sourcelink = True 179 | 180 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 181 | #html_show_sphinx = True 182 | 183 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 184 | #html_show_copyright = True 185 | 186 | # If true, an OpenSearch description file will be output, and all pages will 187 | # contain a tag referring to it. The value of this option must be the 188 | # base URL from which the finished HTML is served. 189 | #html_use_opensearch = '' 190 | 191 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 192 | #html_file_suffix = None 193 | 194 | # Language to be used for generating the HTML full-text search index. 195 | # Sphinx supports the following languages: 196 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 197 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 198 | #html_search_language = 'en' 199 | 200 | # A dictionary with options for the search language support, empty by default. 201 | # Now only 'ja' uses this config value 202 | #html_search_options = {'type': 'default'} 203 | 204 | # The name of a javascript file (relative to the configuration directory) that 205 | # implements a search results scorer. If empty, the default will be used. 206 | #html_search_scorer = 'scorer.js' 207 | 208 | # Output file base name for HTML help builder. 209 | htmlhelp_basename = 'arclibdoc' 210 | 211 | # -- Options for LaTeX output --------------------------------------------- 212 | 213 | latex_elements = { 214 | # The paper size ('letterpaper' or 'a4paper'). 215 | #'papersize': 'letterpaper', 216 | 217 | # The font size ('10pt', '11pt' or '12pt'). 218 | #'pointsize': '10pt', 219 | 220 | # Additional stuff for the LaTeX preamble. 221 | #'preamble': '', 222 | 223 | # Latex figure (float) alignment 224 | #'figure_align': 'htbp', 225 | } 226 | 227 | # Grouping the document tree into LaTeX files. List of tuples 228 | # (source start file, target name, title, 229 | # author, documentclass [howto, manual, or own class]). 230 | latex_documents = [ 231 | (master_doc, 'arclib.tex', u'arclib Documentation', 232 | u'Ryan Gonzalez', 'manual'), 233 | ] 234 | 235 | # The name of an image file (relative to this directory) to place at the top of 236 | # the title page. 237 | #latex_logo = None 238 | 239 | # For "manual" documents, if this is true, then toplevel headings are parts, 240 | # not chapters. 241 | #latex_use_parts = False 242 | 243 | # If true, show page references after internal links. 244 | #latex_show_pagerefs = False 245 | 246 | # If true, show URL addresses after external links. 247 | #latex_show_urls = False 248 | 249 | # Documents to append as an appendix to all manuals. 250 | #latex_appendices = [] 251 | 252 | # If false, no module index is generated. 253 | #latex_domain_indices = True 254 | 255 | 256 | # -- Options for manual page output --------------------------------------- 257 | 258 | # One entry per manual page. List of tuples 259 | # (source start file, name, description, authors, manual section). 260 | man_pages = [ 261 | (master_doc, 'arclib', u'arclib Documentation', 262 | [author], 1) 263 | ] 264 | 265 | # If true, show URL addresses after external links. 266 | #man_show_urls = False 267 | 268 | 269 | # -- Options for Texinfo output ------------------------------------------- 270 | 271 | # Grouping the document tree into Texinfo files. List of tuples 272 | # (source start file, target name, title, author, 273 | # dir menu entry, description, category) 274 | texinfo_documents = [ 275 | (master_doc, 'arclib', u'arclib Documentation', 276 | author, 'arclib', 'One line description of project.', 277 | 'Miscellaneous'), 278 | ] 279 | 280 | # Documents to append as an appendix to all manuals. 281 | #texinfo_appendices = [] 282 | 283 | # If false, no module index is generated. 284 | #texinfo_domain_indices = True 285 | 286 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 287 | #texinfo_show_urls = 'footnote' 288 | 289 | # If true, do not generate a @detailmenu in the "Top" node's menu. 290 | #texinfo_no_detailmenu = False 291 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | arclib 2 | ====== 3 | 4 | Python has a very thorough standard library, including a variety of modules for 5 | compressing and decompressing data. However, all of them have different APIs for 6 | working with them., leading to difficulty in attempting to support multiple 7 | archive formats. arclib is an attempt to fix this by providing a common API that 8 | bridges the various archive modules in Python. 9 | 10 | Rationale 11 | ********* 12 | 13 | A while back, I was trying to port a Python application from using zip files to 14 | using tar files. To say it was "painful" is an understatement. The two modules 15 | have conceptually similar but immensely different APIs. Therefore, I started work 16 | on arclib to provide a unified API between the two, as well as between the other 17 | archive modules in the standard library. 18 | 19 | Archive categories 20 | ****************** 21 | 22 | ``arclib`` divides Python's five archive modules (zipfile_, tarfile_, lzma_, 23 | bzip2_, and gzip_) into three categories: 24 | 25 | - Basic, one-shot: the only compression method in this category is gzip. With 26 | gzip, the only allowed method of compression and decompression is "one-shot", 27 | where all the data must be processed at once. 28 | 29 | - Basic, incremental: LZMA and bzip2 fall under here. These not only allow 30 | one-shot but also incremental compression and decompression, where the data is 31 | fed in in small chunks. 32 | 33 | - Complex, file-system: Tar and zip both are considering "complex" in arclib, in 34 | that they both allow one to store entire directory trees, not just chunks of 35 | data. 36 | 37 | .. _zipfile: https://docs.python.org/3/library/zipfile.html 38 | .. _tarfile: https://docs.python.org/3/library/tarfile.html 39 | .. _lzma: https://docs.python.org/3/library/lzma.html 40 | .. _bzip2: https://docs.python.org/3/library/bzip2.html 41 | .. _gzip: https://docs.python.org/3/library/gzip.html 42 | 43 | Modules 44 | ******* 45 | 46 | arclib contains the following modules: 47 | 48 | Basic compression/decompression 49 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 50 | 51 | - **arclib.gz:** A module that exposes a basic, one-shot gzip 52 | compression/decompression API. 53 | - **arclib.bz2**: A module that exposes both a basic, one-shot bzip2 54 | compression/decompression and an incremental API. 55 | - **arclib.lzma**: A module that exposes both a basic, one-shot LZMA 56 | compression/decompression and an incremental API. 57 | 58 | **Note that arclib.gz does not expose an incremental API.** 59 | 60 | Complex, file-system compression/decompression 61 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 62 | 63 | Both **arclib.zip** and **arclib.tar** expose an API that allows for manipulation 64 | of their respective complex archive formats. 65 | 66 | The APIs 67 | ******** 68 | 69 | Base 70 | ^^^^ 71 | 72 | All modules in arclib implement this API: 73 | 74 | .. py:function:: open(*args, **kw) 75 | 76 | Returns a ``File`` with the given arguments. 77 | 78 | Basic, one-shot 79 | ^^^^^^^^^^^^^^^ 80 | 81 | All the "basic" modules (gz, bz2, and lzma) implement this API: 82 | 83 | detach, truncate 84 | https://docs.python.org/3/library/io.html#io.BufferedIOBase 85 | 86 | .. py:function:: compress(data) 87 | 88 | Compresses data using the corresponding compression method. 89 | 90 | :param bytes data: The data to compress. 91 | :return: The compressed data. 92 | :rtype: bytes 93 | 94 | .. py:function:: decompress(data) 95 | 96 | Decompresses data using the corresponding decompression method. 97 | 98 | :param bytes data: The data to decompress. 99 | :return: The decompressed data. 100 | :rtype: bytes 101 | 102 | In addition, their ``open`` function is an alias for the corresponding Python 103 | module's ``open`` and therefore returns the module's ``File``. For instance, 104 | ``arclib.gz.open`` is an alias for Python's own ``gzip.open`` and returns 105 | ``gzip.GzipFile``. 106 | 107 | Basic, incremental 108 | ^^^^^^^^^^^^^^^^^^ 109 | 110 | Both arclib.bz2 and arclib.lzma (***not*** arclib.gz) implement this API. 111 | 112 | .. py:class:: Compressor 113 | 114 | A class that implements incremental compression. All types of this kind are 115 | instances of ``arclib.AbstractBasicCompressor``. Example usage: 116 | 117 | .. code-block:: python 118 | 119 | my_compressor = arclib.bz2.Compressor() # The compressor object. 120 | compressed_data = b'' # The resulting compressed data. 121 | compressed_data += my_compressor.compress(b'Something to compress...') 122 | compressed_data += my_compressor.compress(b'More stuff!') 123 | compressed_data += my_compressor.flush() # Always remember the flush call! 124 | 125 | .. py:method:: compress(data) 126 | 127 | Incrementally compresses data using the corresponding compression method. 128 | 129 | :param bytes data: The data to compress. 130 | :return: A portion of compressed data, or an empty byte string. Note that 131 | this data is **not** considered valid on its own, and must be 132 | combined with both other calls to ``compress`` and the result of 133 | :py:meth:`flush`. 134 | :rtype: bytes 135 | 136 | 137 | .. py:method:: flush() 138 | 139 | Flushes the compressor's internal buffers. 140 | 141 | :return: The rest of the compressed data. 142 | :rtype: bytes 143 | 144 | .. py:class:: Decompressor 145 | 146 | A class that implements incremental compression. All types of this kind are 147 | instances of ``arclib.AbstractBasicDecompressor``. Example usage: 148 | 149 | .. code-block:: python 150 | 151 | compressed_data = arclib.bz2.compress(b'Some data to compress!') 152 | my_decompressor = arclib.bz2.Decompressor() # The decompressor object. 153 | decompressed_data = b'' # The resulting decompressed data. 154 | # Decompress some data. 155 | decompressed_data += my_decompressor.decompress(compressed_data[:5]) 156 | # And some more data! 157 | decompressed_data += my_decompressor.decompress(compressed_data[5:]) 158 | assert decompressed_data == b'Some data to compress!' 159 | assert my_decompressor.eof 160 | 161 | .. py:method:: decompress(data) 162 | 163 | Incrementally decompresses data using the corresponding decompression 164 | method. 165 | 166 | :param bytes data: The data to decompress. 167 | :return: A portion of decompressed data, or an empty byte string. Note that 168 | this data is **not** the complete decompressed data, and must b 169 | combined with other calls to ``decompress``. 170 | :rtype: bytes 171 | 172 | .. py:attribute:: eof 173 | 174 | Whether or not the end of the compressed data has been reached. 175 | 176 | .. py:attribute:: unused_data 177 | 178 | Any unused data left over after the decompression completed. 179 | 180 | Complex 181 | ^^^^^^^ 182 | 183 | Both arclib.zip and arclib.tar implement this API. 184 | 185 | .. py:function:: open(*args, **kw) 186 | 187 | Opens an archive. All arguments are passed to the corresponding function; for 188 | instance, ``arclib.zip.open`` passes all its arguments to ``zipfile.open``. 189 | 190 | :return: The opened archive file. 191 | :rtype: :py:class:`File` 192 | 193 | .. py:function:: openobj(fileobj, **kw) 194 | 195 | Opens the given file object. Whereas *open* opens a file path, *openobj* opens an 196 | in-memory file object. 197 | 198 | :return: The opened archive file. 199 | :rtype: :py:class:`File` 200 | 201 | .. py:class:: File 202 | 203 | An opened archive file. Can be used as a context manager. Example: 204 | 205 | .. code-block:: python 206 | 207 | import arclib.zip 208 | 209 | with arclib.zip.open('myfile.zip') as f: 210 | # Stuff here. 211 | # f is automatically closed. 212 | 213 | .. py:method:: close() 214 | 215 | Close the archive file. 216 | 217 | .. py:method:: info_for(member) 218 | 219 | Returns an :py:class:`Info` object containing information about the given 220 | archive member. 221 | 222 | :param str member: A string describing the path to the archive member, e.g. 223 | ``x/y/z``. 224 | :return: The member information object. 225 | :rtype: :py:class:`Info` 226 | 227 | .. py:method:: all_info() 228 | 229 | Retrieves :py:class:`Info` objects for all the archive's members. 230 | 231 | :return: A list of all the :py:class:`Info` objects for all the archive's 232 | members. 233 | :rtype: list of :py:class:`Info` 234 | 235 | .. py:method:: members() 236 | 237 | Retrieves all the archive's members. 238 | 239 | :return: A list of strings, one for each archive member. 240 | :rtype: list of str 241 | 242 | .. py:method:: dump() 243 | 244 | Dump a description archive's contents to standard output. 245 | 246 | .. py:method:: add(path, arcname=None, recursive=True) 247 | 248 | Adds a file or directory to the archive. 249 | 250 | :param str path: The path to add to the archive. 251 | :param str arcname: The name to give the file when placing it in the 252 | archive. If ``None``, then it will be the same as 253 | *path*, but with leading roots and the drive removed. 254 | :param bool recursive: If *path* is a directory and this is a truthy value, 255 | then the directory's contents will also be added to 256 | the archive. 257 | 258 | .. py:method:: add_data(path, data) 259 | 260 | Adds a ``bytes`` object to the archive. 261 | 262 | :param str path: The name to give the file when placing it in the archive. 263 | :param bytes data: The file's contents. 264 | 265 | .. py:method:: extract(member, path=None) 266 | 267 | Extracts a member from the archive. 268 | 269 | :param str member: The member to extract. 270 | :param str path: The target path to extract the member to; if ``None``, then 271 | it will be the current directory. 272 | 273 | ``arclib.zip.File.extract`` also takes the following keyword argument: 274 | 275 | :param str pwd: The password to use to extract the file, or ``None``. 276 | 277 | .. py:method:: open(member, universal_newlines=False) 278 | 279 | Extracts a member from the archive into memory rather that onto the disk. Returns 280 | a bytes file-like object with the following properties: 281 | 282 | - *name* - The name of the member. 283 | - *read(size=-1)* - Read and return *size* bytes from the file. 284 | 285 | If ``universal_newlines`` is ``True``, then the file object will be an instance of 286 | ``io.TextIOWrapper`` that also has the ``name`` property. 287 | 288 | :param str member: The member to extract. 289 | :param str universal_newlines: If ``True``, returns an ``io.TextIOWrapper`` that 290 | also has a property *name*, which is the name of the 291 | member. Otherwise, returns a file-like object as 292 | mentioned above. 293 | 294 | ``arclib.zip.File.extract`` also takes the following keyword argument: 295 | 296 | :param str pwd: The password to use to extract the file, or ``None``. 297 | :return: The file-like object as explained above, if the member is present. If it 298 | is not present, returns ``None``. 299 | 300 | .. py:class:: Info 301 | 302 | An object containing information about an archive member. 303 | 304 | .. py:attribute:: info 305 | 306 | The underlying, "true" info object. With ``arclib.zip.Info``, this is an 307 | instance of ``zipfile.ZipInfo``; with ``arclib.tar.Info``, this is an 308 | instance of ``tarfile.TarInfo``. 309 | 310 | .. py:attribute:: filename 311 | 312 | The name of the file within the archive. 313 | 314 | .. py:attribute:: size 315 | 316 | The number of bytes that the file takes up within the archive. 317 | 318 | .. py:attribute:: mtime 319 | 320 | A ``datetime.datetime`` object containing the last modification time of the 321 | file. 322 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | with open('README.rst') as f: 4 | descr = f.read() 5 | 6 | setup(name='arclib', 7 | version='0.2.1', 8 | description="A unified API for accessing Python's compression formats", 9 | long_description=descr, 10 | author='Ryan Gonzalez', 11 | author_email='rymg19@gmail.com', 12 | url='https://github.com/kirbyfan64/arclib', 13 | packages=['arclib'], 14 | classifiers=[ 15 | 'Programming Language :: Python :: 3 :: Only', 16 | 'Topic :: System :: Archiving', 17 | 'License :: OSI Approved :: MIT License', 18 | ], 19 | ) 20 | -------------------------------------------------------------------------------- /tests/__pycache__/test_bz2.cpython-27-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/refi64/arclib/a872904fa4f4270b851b2e96c917d26d3c0a0a4c/tests/__pycache__/test_bz2.cpython-27-PYTEST.pyc -------------------------------------------------------------------------------- /tests/__pycache__/test_bz2.cpython-34-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/refi64/arclib/a872904fa4f4270b851b2e96c917d26d3c0a0a4c/tests/__pycache__/test_bz2.cpython-34-PYTEST.pyc -------------------------------------------------------------------------------- /tests/__pycache__/test_gz.cpython-27-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/refi64/arclib/a872904fa4f4270b851b2e96c917d26d3c0a0a4c/tests/__pycache__/test_gz.cpython-27-PYTEST.pyc -------------------------------------------------------------------------------- /tests/__pycache__/test_gz.cpython-34-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/refi64/arclib/a872904fa4f4270b851b2e96c917d26d3c0a0a4c/tests/__pycache__/test_gz.cpython-34-PYTEST.pyc -------------------------------------------------------------------------------- /tests/__pycache__/test_lzma.cpython-27-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/refi64/arclib/a872904fa4f4270b851b2e96c917d26d3c0a0a4c/tests/__pycache__/test_lzma.cpython-27-PYTEST.pyc -------------------------------------------------------------------------------- /tests/__pycache__/test_lzma.cpython-34-PYTEST.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/refi64/arclib/a872904fa4f4270b851b2e96c917d26d3c0a0a4c/tests/__pycache__/test_lzma.cpython-34-PYTEST.pyc -------------------------------------------------------------------------------- /tests/__pycache__/util.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/refi64/arclib/a872904fa4f4270b851b2e96c917d26d3c0a0a4c/tests/__pycache__/util.cpython-34.pyc -------------------------------------------------------------------------------- /tests/test_bz2.py: -------------------------------------------------------------------------------- 1 | from util import * 2 | from arclib import bz2 3 | from bz2 import compress, decompress 4 | 5 | def test_incremental_compress(): 6 | basic_test_c(bz2.Compressor(), decompress) 7 | 8 | def test_incremental_decompress(): 9 | basic_test_d(bz2.Decompressor(), compress) 10 | -------------------------------------------------------------------------------- /tests/test_lzma.py: -------------------------------------------------------------------------------- 1 | from util import * 2 | from arclib import lzma 3 | from lzma import compress, decompress 4 | 5 | def test_incremental_compress(): 6 | basic_test_c(lzma.Compressor(), decompress) 7 | 8 | def test_incremental_decompress(): 9 | basic_test_d(lzma.Decompressor(), compress) 10 | -------------------------------------------------------------------------------- /tests/util.py: -------------------------------------------------------------------------------- 1 | import sys, os, string 2 | sys.path.insert(0, os.path.abspath('.')) 3 | letters = bytes(string.ascii_letters, 'ascii') 4 | 5 | def basic_test_c(c, d): 6 | res = b'' 7 | for b in letters: 8 | res += c.compress(bytes([b])) 9 | res += c.flush() 10 | assert d(res) == letters 11 | 12 | def basic_test_d(d, c): 13 | res = b'' 14 | for b in c(bytes(letters)): 15 | res += d.decompress(bytes([b])) 16 | assert res == letters 17 | assert not d.unused_data 18 | -------------------------------------------------------------------------------- /tests/util.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/refi64/arclib/a872904fa4f4270b851b2e96c917d26d3c0a0a4c/tests/util.pyc --------------------------------------------------------------------------------