From db6dad845f2cc44669e5130f2c6f6411d2ef8108 Mon Sep 17 00:00:00 2001 From: TheOnePath Date: Sun, 11 Jun 2023 21:11:33 +0100 Subject: [PATCH] Updated extractar.py using attrs The ArchiveInfo class now uses the attrs library and slots over instance dictionaries. Class now explicitly inherits from class object for custom setattr dunder method in AchiveInfo. This override allows for immutability of class attributes, and flexible mutability when appropriate. --- extractar.py | 51 +++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/extractar.py b/extractar.py index 52e65d3..1621f65 100644 --- a/extractar.py +++ b/extractar.py @@ -9,7 +9,8 @@ # list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +# this list of conditions and the following disclaimer in the documentation and/or other materials provided with the +# distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software @@ -30,6 +31,8 @@ import os import struct import textwrap import pathlib +from typing import Any +import attr __version__ = '1.0' @@ -56,8 +59,8 @@ class ArchiveBufferReadError(IndexError): Debian binary package entry information. Exception is of type IndexError. """ - -class ArchiveInfo: +@attr.s(slots=True) +class ArchiveInfo(object): """ Information on a file in an archive. @@ -76,19 +79,13 @@ class ArchiveInfo: the same name are present; if you change the `name` attribute, the initial file will be extracted with the new name (and new metadata). """ - def __init__(self, name: bytes, size: int, mtime: int, perms: int, uid: int, gid: int): - self._name = name - self.size = size - self.mtime = mtime - self.perms = perms - self.uid = uid - self.gid = gid - self.offset: int = 0 - - - @property - def name(self): - return self._name + name: bytes = attr.ib(converter=lambda s: _utf8(s).rstrip(b' '), on_setattr=attr.setters.NO_OP) + size: int = attr.ib(converter=int) + mtime: int = attr.ib(converter=int) + perms: int = attr.ib(converter=lambda x: int(x, 8)) + uid: int = attr.ib(converter=int) + gid: int = attr.ib(converter=int) + offset: int = attr.ib(converter=int, default=0) @classmethod @@ -111,19 +108,30 @@ class ArchiveInfo: if magic != b'\x60\n': raise ValueError("Invalid file signature") - return cls(_utf8(name).rstrip(b' '), int(size, 10), int(mtime, 10), int(perms, 8), int(uid, 10), int(gid, 10)) + return cls(name=name, size=size, mtime=mtime, perms=perms, uid=uid, gid=gid) + + + def __setattr__(self, __name: str, __value: Any) -> None: + """ + Custom setattr dunder method to create strict immutability over certain attributes of a class. Any attributes + which may be mutable should be handled appropriately. + """ + # |------ immutable ------| |---- mutable ----| + if not hasattr(self, __name) or __name == "offset": + object.__setattr__(self, __name, __value) def __repr__(self) -> str: return textwrap.dedent('''\ - ArchiveInfo: ({0}) + {7}: ({0}) _name: {0} size: {1} mtime: {2} perms: {3} uid: {4} gid: {5} - offset: {6}\n'''.format(self._name, self.size, self.mtime, self.perms, self.uid, self.gid, self.offset)) + offset: {6}\n'''.format(self.name, self.size, self.mtime, self.perms, self.uid, self.gid, self.offset, + self.__class__)) class Archive: @@ -265,9 +273,9 @@ class Archive: # write out the specific content of an archive member pos = member.offset + 60 + # @TODO: put in try cus IO be IO lmao with open(path, 'wb') as fp: - data = self.__ar_contents[pos:pos+member.size] - fp.write(data) + fp.write(self.__ar_contents[pos:pos+member.size]) def extractall(self, path: str | bytes = ''): @@ -287,4 +295,3 @@ class Archive: # iterate over all members in the mapping for name in self._mapping.keys(): self.extract(name, os.path.join(_utf8(path), name)) -