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.
This commit is contained in:
Ethan Smith-Coss 2023-06-11 21:11:33 +01:00
parent 50443715fc
commit db6dad845f
Signed by: TheOnePath
GPG Key ID: 4E7D436CE1A0BAF1

View File

@ -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))