PK!docci/__init__.pyPK!WLdocci/config.py""" Constants required for lib """ import os BASE_DIR = os.path.dirname(os.path.dirname(__file__)) TEST_DATA_DIR = os.path.join(BASE_DIR, "test_data") PK!Yb6 docci/file.py""" Utils for file manipulations like extracting file name from path """ import base64 import io import os from dataclasses import dataclass, field from typing import Optional, Dict, Union from urllib.parse import urlencode def extract_file_name(path: str) -> str: """ >>> extract_file_name("tests/test_api.py") 'test_api.py' """ return os.path.basename(path) @dataclass class FileAttachment: """ Class for file abstraction """ name: str content: bytes = field(repr=False) @property def name_without_extension(self) -> str: """ >>> FileAttachment("sample.py", b"").name_without_extension 'sample' """ return self.name.rsplit(".", 1)[0] @property def extension(self) -> str: """ >>> FileAttachment("sample.py", b"").extension 'py' """ return self.name.rsplit(".", 1)[-1] @property def content_stream(self) -> io.BytesIO: """Return file attachment content as bytes stream""" return io.BytesIO(self.content) @property def content_base64(self) -> bytes: """Convert content to base64 binary string""" return base64.b64encode(self.content) @property def content_disposition(self) -> Dict[str, str]: """ Convert file name to urlencoded Content-Disposition header >>> FileAttachment("sample.py", b"").content_disposition {'Content-Disposition': 'attachment; filename=sample.py'} """ file_name = urlencode({"filename": self.name}) return {"Content-Disposition": f'attachment; {file_name}'} def save(self, path: Optional[str] = None) -> None: """ Save file to disk """ path = path or self.name with open(path, "wb") as f: f.write(self.content) @classmethod def load(cls, path: str) -> 'FileAttachment': """ Load file from disk """ assert os.path.exists(path), f'No such file: "{path}"' with open(path, "rb") as f: return FileAttachment(extract_file_name(path), f.read()) @classmethod def load_from_base64(cls, base64_str: Union[str, bytes], name: str) -> 'FileAttachment': """ Load file from base64 string """ return FileAttachment(name, base64.b64decode(base64_str)) PK!lA## docci/zip.py""" Utils for working with zip archives """ import io from typing import Union, Iterable, Sequence from zipfile import ZipFile from docci.file import FileAttachment def list_zip_files( zip_file: Union[str, io.BytesIO, ZipFile, FileAttachment] ) -> Sequence[FileAttachment]: """ List zip archive files """ def zip_file_generator(zip_file: ZipFile) -> Iterable[FileAttachment]: for filename in zip_file.namelist(): content = zip_file.read(filename) yield FileAttachment(filename, content) if isinstance(zip_file, FileAttachment): zip_file = ZipFile(zip_file.content_stream) if isinstance(zip_file, (str, io.BytesIO)): zip_file = ZipFile(zip_file) return list(zip_file_generator(zip_file)) PK!HڽTUdocci-0.2.0.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK!H٦docci-0.2.0.dist-info/METADATAMOA +&"LH (>֥|8Q Gm>};2!z8Ƃ*~1(b5rm":*[p$3.oAp($] &?enYkϰO8G-!}.^2FHj2+^^ QLJ1RkyqѿB?R1g;⋫6ީdOsB| V߼PK!HH'(Vg:Z3! a+!]aor"Ye$"g^48s'LCx,5q]et=sfF:vúZl4c v h &EHe;:YgKX{)zW4ϋLR$EU S2 BGRJ~ʁJN1GA!4vgVY=,Ӝ=~_N׽PݯZ3ܸLA ~;I]sa`ca<+!o;\–glkaRwU&Z3ukb Xs`AxPK!docci/__init__.pyPK!WL/docci/config.pyPK!Yb6 docci/file.pyPK!lA## docci/zip.pyPK!HڽTUdocci-0.2.0.dist-info/WHEELPK!H٦docci-0.2.0.dist-info/METADATAPK!HH