11from contextlib import contextmanager
22from ctypes import byref , cast , c_char , c_size_t , c_void_p , POINTER
3+ from posixpath import join
34import warnings
45
56from . import ffi
6- from .entry import ArchiveEntry
7+ from .entry import ArchiveEntry , FileType
78from .ffi import (
89 OPEN_CALLBACK , WRITE_CALLBACK , CLOSE_CALLBACK , NO_OPEN_CB , NO_CLOSE_CB ,
9- REGULAR_FILE , DEFAULT_UNIX_PERMISSION , ARCHIVE_EOF ,
10+ ARCHIVE_EOF ,
1011 page_size , entry_sourcepath , entry_clear , read_disk_new , read_disk_open_w ,
1112 read_next_header2 , read_disk_descend , read_free , write_header , write_data ,
12- write_finish_entry , entry_set_size , entry_set_filetype , entry_set_perm ,
13+ write_finish_entry ,
1314 read_disk_set_behavior
1415)
1516
@@ -42,26 +43,55 @@ def add_entries(self, entries):
4243 write_data (write_p , block , len (block ))
4344 write_finish_entry (write_p )
4445
45- def add_files (self , * paths , ** kw ):
46- """Read the given paths from disk and add them to the archive.
46+ def add_files (
47+ self , * paths , flags = 0 , lookup = False , pathname = None , ** attributes
48+ ):
49+ """Read files through the OS and add them to the archive.
4750
48- The keyword arguments (`**kw`) are passed to `new_archive_read_disk`.
51+ Args:
52+ paths (str): the paths of the files to add to the archive
53+ flags (int):
54+ passed to the C function `archive_read_disk_set_behavior`;
55+ use the `libarchive.flags.READDISK_*` constants
56+ lookup (bool):
57+ when True, the C function `archive_read_disk_set_standard_lookup`
58+ is called to enable the lookup of user and group names
59+ pathname (str | None):
60+ the path of the file in the archive, defaults to the source path
61+ attributes (dict): passed to `ArchiveEntry.modify()`
62+
63+ Raises:
64+ ArchiveError: if a file doesn't exist or can't be accessed, or if
65+ adding it to the archive fails
4966 """
5067 write_p = self ._pointer
5168
5269 block_size = ffi .write_get_bytes_per_block (write_p )
5370 if block_size <= 0 :
5471 block_size = 10240 # pragma: no cover
5572
56- entry = ArchiveEntry (None )
73+ entry = ArchiveEntry ()
5774 entry_p = entry ._entry_p
75+ destination_path = attributes .pop ('pathname' , None )
5876 for path in paths :
59- with new_archive_read_disk (path , ** kw ) as read_p :
77+ with new_archive_read_disk (path , flags , lookup ) as read_p :
6078 while 1 :
6179 r = read_next_header2 (read_p , entry_p )
6280 if r == ARCHIVE_EOF :
6381 break
64- entry .pathname = entry .pathname .lstrip ('/' )
82+ entry_path = entry .pathname
83+ if destination_path :
84+ if entry_path == path :
85+ entry_path = destination_path
86+ else :
87+ assert entry_path .startswith (path )
88+ entry_path = join (
89+ destination_path ,
90+ entry_path [len (path ):].lstrip ('/' )
91+ )
92+ entry .pathname = entry_path .lstrip ('/' )
93+ if attributes :
94+ entry .modify (** attributes )
6595 read_disk_descend (read_p )
6696 write_header (write_p , entry_p )
6797 if entry .isreg :
@@ -74,34 +104,24 @@ def add_files(self, *paths, **kw):
74104 write_finish_entry (write_p )
75105 entry_clear (entry_p )
76106
107+ def add_file (self , path , ** kw ):
108+ "Single-path alias of `add_files()`"
109+ return self .add_files (path , ** kw )
110+
77111 def add_file_from_memory (
78112 self , entry_path , entry_size , entry_data ,
79- filetype = REGULAR_FILE , permission = DEFAULT_UNIX_PERMISSION ,
80- atime = None , mtime = None , ctime = None , birthtime = None ,
81- uid = None , gid = None ,
113+ filetype = FileType .REGULAR_FILE , permission = 0o664 ,
114+ ** other_attributes
82115 ):
83116 """"Add file from memory to archive.
84117
85118 Args:
86119 entry_path (str): the file's path
87120 entry_size (int): the file's size, in bytes
88121 entry_data (bytes | Iterable[bytes]): the file's content
89- filetype (int): the file's type (normal, symlink, etc.)
90- permission (int): the file's permissions
91- atime (int | Tuple[int]):
92- the file's most recent access time,
93- in seconds or as a tuple (seconds, nanoseconds)
94- mtime (int | Tuple[int]):
95- the file's most recent modification time,
96- in seconds or as a tuple (seconds, nanoseconds)
97- ctime (int | Tuple[int]):
98- the file's creation time,
99- in seconds or as a tuple (seconds, nanoseconds)
100- birthtime (int | Tuple[int]):
101- the file's birth time (for archive formats that support it),
102- in seconds or as a tuple (seconds, nanoseconds)
103- uid (int): the file owner's identifier
104- gid (int): the file group's identifier
122+ filetype (int): see `libarchive.entry.ArchiveEntry.modify()`
123+ permission (int): see `libarchive.entry.ArchiveEntry.modify()`
124+ other_attributes: see `libarchive.entry.ArchiveEntry.modify()`
105125 """
106126 archive_pointer = self ._pointer
107127
@@ -112,36 +132,11 @@ def add_file_from_memory(
112132 "entry_data: expected bytes, got %r" % type (entry_data )
113133 )
114134
115- archive_entry = ArchiveEntry (None )
116- archive_entry_pointer = archive_entry ._entry_p
117-
118- archive_entry .pathname = entry_path
119- entry_set_size (archive_entry_pointer , entry_size )
120- entry_set_filetype (archive_entry_pointer , filetype )
121- entry_set_perm (archive_entry_pointer , permission )
122-
123- if uid is not None :
124- archive_entry .uid = uid
125- if gid is not None :
126- archive_entry .gid = gid
127-
128- if atime is not None :
129- if not isinstance (atime , tuple ):
130- atime = (atime , 0 )
131- archive_entry .set_atime (* atime )
132- if mtime is not None :
133- if not isinstance (mtime , tuple ):
134- mtime = (mtime , 0 )
135- archive_entry .set_mtime (* mtime )
136- if ctime is not None :
137- if not isinstance (ctime , tuple ):
138- ctime = (ctime , 0 )
139- archive_entry .set_ctime (* ctime )
140- if birthtime is not None :
141- if not isinstance (birthtime , tuple ):
142- birthtime = (birthtime , 0 )
143- archive_entry .set_birthtime (* birthtime )
144- write_header (archive_pointer , archive_entry_pointer )
135+ entry = ArchiveEntry (
136+ pathname = entry_path , size = entry_size , filetype = filetype ,
137+ perm = permission , ** other_attributes
138+ )
139+ write_header (archive_pointer , entry ._entry_p )
145140
146141 for chunk in entry_data :
147142 if not chunk :
0 commit comments