Skip to content

Commit 2513a94

Browse files
committed
Small update
1 parent 6b42b88 commit 2513a94

3 files changed

Lines changed: 124 additions & 74 deletions

File tree

File renamed without changes.
File renamed without changes.

pyneofile/pyfile.py

Lines changed: 124 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import hmac
2626
import json
2727
import stat
28+
import atexit
2829
import shutil
2930
import base64
3031
import logging
@@ -445,6 +446,97 @@ def add_format(reg, key, magic, ext, name=None, ver="001",
445446
"format_extension": ext,
446447
}
447448

449+
# Process-lifetime extract dir (only used when resources aren't on the filesystem)
450+
_EXTRACT_DIR = None
451+
452+
def _get_extract_dir():
453+
global _EXTRACT_DIR
454+
if _EXTRACT_DIR is None:
455+
_EXTRACT_DIR = tempfile.mkdtemp(prefix="gm2k-cfg-")
456+
atexit.register(lambda: shutil.rmtree(_EXTRACT_DIR, ignore_errors=True))
457+
return _EXTRACT_DIR
458+
459+
def _atomic_write(path, data):
460+
tmp = path + ".tmp"
461+
f = open(tmp, "wb")
462+
try:
463+
f.write(data)
464+
finally:
465+
f.close()
466+
467+
try:
468+
# Py3
469+
os.replace(tmp, path)
470+
except Exception:
471+
# Py2 / fallback
472+
try:
473+
if os.path.exists(path):
474+
os.remove(path)
475+
except Exception:
476+
pass
477+
os.rename(tmp, path)
478+
479+
def resource_path(package_name, filename):
480+
"""
481+
Return a REAL filesystem path to a resource inside this package.
482+
483+
- If package is installed normally on disk: returns the existing path (no copy).
484+
- If package is imported from zip/egg: extracts to a temp dir once and returns that path.
485+
- Falls back to pkg_resources if needed.
486+
"""
487+
files = None
488+
try:
489+
try:
490+
from importlib.resources import files as _files # Py3.9+ (and sometimes available)
491+
files = _files
492+
except Exception:
493+
from importlib_resources import files as _files # backport
494+
files = _files
495+
except Exception:
496+
files = None
497+
498+
if files is not None:
499+
try:
500+
ref = files(package_name).joinpath(filename)
501+
502+
# If backed by filesystem, return that path
503+
try:
504+
return os.fspath(ref) # Py3 only
505+
except Exception:
506+
# zipped/non-path traversable -> extract bytes
507+
out = os.path.join(_get_extract_dir(), filename)
508+
if not os.path.exists(out):
509+
data = ref.read_bytes()
510+
_atomic_write(out, data)
511+
return out
512+
except Exception:
513+
pass
514+
515+
# setuptools fallback
516+
try:
517+
import pkg_resources
518+
return pkg_resources.resource_filename(package_name, filename)
519+
except Exception:
520+
pass
521+
522+
# last resort: __file__ relative (works only when not zipped)
523+
mod = sys.modules.get(package_name)
524+
base = os.path.dirname(getattr(mod, "__file__", __file__))
525+
return os.path.join(base, filename)
526+
527+
def resource_dir(package_name, filenames):
528+
"""
529+
Ensure a set of resources exist as real files in one directory.
530+
Returns that directory path.
531+
"""
532+
paths = [resource_path(package_name, fn) for fn in filenames]
533+
return os.path.dirname(paths[0])
534+
535+
filecfgpath = resource_dir(__name__, [
536+
"neofile.ini",
537+
"neofile.json",
538+
])
539+
448540
__file_format_multi_dict__ = {}
449541
__file_format_default__ = "NeoFile"
450542
__include_defaults__ = True
@@ -466,9 +558,9 @@ def add_format(reg, key, magic, ext, name=None, ver="001",
466558
__program_name__ = "Py"+__file_format_default__
467559
__use_env_file__ = True
468560
__use_ini_file__ = True
469-
__use_ini_name__ = "neofile.ini"
561+
__use_ini_name__ = os.path.join(filecfgpath, "neofile.ini")
470562
__use_json_file__ = False
471-
__use_json_name__ = "neofile.json"
563+
__use_json_name__ = os.path.join(filecfgpath, "neofile.json")
472564
if(__use_ini_file__ and __use_json_file__):
473565
__use_json_file__ = False
474566
if('PYARCHIVEFILE_CONFIG_FILE' in os.environ and os.path.exists(os.environ['PYARCHIVEFILE_CONFIG_FILE']) and __use_env_file__):
@@ -3298,45 +3390,34 @@ def UncompressFileAlt(fp, formatspecs=__file_format_multi_dict__, filestart=0):
32983390
if IsNestedDict(formatspecs) and kind in formatspecs:
32993391
formatspecs = formatspecs[kind]
33003392

3301-
# Guard against detector side-effects: ensure we're back at filestart
3302-
try:
3303-
src.seek(filestart, 0)
3304-
except Exception:
3305-
pass
3393+
src.seek(filestart, 0)
33063394

33073395
# Build logical stream (or passthrough)
33083396
if kind == "gzip" and "gzip" in compressionsupport:
33093397
wrapped = gzip.GzipFile(fileobj=src, mode="rb")
3398+
wrapped.seek(0, 0)
33103399
elif kind == "bzip2" and ("bzip2" in compressionsupport or "bz2" in compressionsupport):
33113400
wrapped = bz2.BZ2File(src)
3401+
wrapped.seek(0, 0)
33123402
elif kind in ("lzma","xz") and (("lzma" in compressionsupport) or ("xz" in compressionsupport)):
33133403
wrapped = lzma.LZMAFile(src)
3404+
wrapped.seek(0, 0)
33143405
elif kind == "zstd" and ("zstd" in compressionsupport or "zstandard" in compressionsupport):
33153406
if 'zstd' in compressionsupport:
33163407
wrapped = zstd.ZstdFile(src, mode="rb")
3408+
wrapped.seek(0, 0)
33173409
else:
33183410
return False
33193411
elif kind == "lz4" and "lz4" in compressionsupport:
33203412
wrapped = lz4.frame.LZ4FrameFile(src, mode="rb")
3413+
wrapped.seek(0, 0)
33213414
elif kind == "zlib" and "zlib" in compressionsupport:
33223415
wrapped = ZlibFile(fileobj=src, mode="rb")
3416+
wrapped.seek(0, 0)
33233417
else:
33243418
# Passthrough
33253419
wrapped = src
3326-
try:
3327-
wrapped.seek(filestart, 0)
3328-
except Exception:
3329-
pass
3330-
kind = "" # treat as uncompressed for logic below
3331-
3332-
# Positioning: start-of-member for compressed; filestart for passthrough
3333-
try:
3334-
if kind in compressionsupport:
3335-
wrapped.seek(0, 0)
3336-
else:
3337-
wrapped.seek(filestart, 0)
3338-
except Exception:
3339-
pass
3420+
wrapped.seek(filestart, 0)
33403421

33413422
return wrapped
33423423

@@ -3351,33 +3432,34 @@ def UncompressFile(infile, formatspecs=__file_format_multi_dict__, mode="rb",
33513432
# Compressed branches
33523433
if (compresscheck == "gzip" and "gzip" in compressionsupport):
33533434
fp = gzip.open(infile, mode)
3435+
fp.seek(0, 0)
33543436
elif (compresscheck == "bzip2" and "bzip2" in compressionsupport):
33553437
fp = bz2.open(infile, mode)
3438+
fp.seek(0, 0)
33563439
elif (compresscheck == "zstd" and "zstandard" in compressionsupport):
33573440
if 'zstd' in compressionsupport:
33583441
fp = zstd.ZstdFile(infile, mode=mode)
3442+
fp.seek(0, 0)
33593443
else:
33603444
return False
33613445
elif (compresscheck == "lz4" and "lz4" in compressionsupport):
33623446
fp = lz4.frame.open(infile, mode)
3447+
fp.seek(0, 0)
33633448
elif ((compresscheck == "lzma" or compresscheck == "xz") and "xz" in compressionsupport):
33643449
fp = lzma.open(infile, mode)
3450+
fp.seek(0, 0)
33653451
elif (compresscheck == "zlib" and "zlib" in compressionsupport):
33663452
fp = ZlibFile(infile, mode=mode)
3453+
fp.seek(0, 0)
33673454

33683455
# Uncompressed (or unknown): open plain file
33693456
else:
33703457
fp = open(infile, mode)
3458+
fp.seek(filestart, 0)
33713459

33723460
except FileNotFoundError:
33733461
return False
33743462

3375-
# Position to filestart if caller requested it (mainly for fileobj-based headers)
3376-
try:
3377-
fp.seek(0 if compresscheck else filestart, 0)
3378-
except Exception:
3379-
pass
3380-
33813463
return fp
33823464

33833465
def CompressOpenFileAlt(fp, compression="auto", compressionlevel=None,
@@ -4588,12 +4670,6 @@ def ReadFileDataWithContent(fp, filestart=0, listonly=False, contentasfile=False
45884670
return False
45894671
delimiter = formatspecs['format_delimiter']
45904672
curloc = filestart
4591-
try:
4592-
fp.seek(0, 2)
4593-
except (OSError, ValueError):
4594-
SeekToEndOfFile(fp)
4595-
CatSize = fp.tell()
4596-
CatSizeEnd = CatSize
45974673
fp.seek(curloc, 0)
45984674
inheaderver = str(int(formatspecs['format_ver'].replace(".", "")))
45994675
headeroffset = fp.tell()
@@ -4652,6 +4728,8 @@ def ReadFileDataWithContent(fp, filestart=0, listonly=False, contentasfile=False
46524728
break
46534729
flist.append(HeaderOut)
46544730
countnum = countnum + 1
4731+
CatSize = fp.tell()
4732+
CatSizeEnd = CatSize
46554733
return flist
46564734

46574735

@@ -4660,12 +4738,6 @@ def ReadFileDataWithContentToArray(fp, filestart=0, seekstart=0, seekend=0, list
46604738
return False
46614739
delimiter = formatspecs['format_delimiter']
46624740
curloc = filestart
4663-
try:
4664-
fp.seek(0, 2)
4665-
except (OSError, ValueError):
4666-
SeekToEndOfFile(fp)
4667-
CatSize = fp.tell()
4668-
CatSizeEnd = CatSize
46694741
fp.seek(curloc, 0)
46704742
inheaderver = str(int(formatspecs['format_ver'].replace(".", "")))
46714743
headeroffset = fp.tell()
@@ -4827,7 +4899,7 @@ def ReadFileDataWithContentToArray(fp, filestart=0, seekstart=0, seekend=0, list
48274899
return False
48284900
formversions = re.search('(.*?)(\\d+)', formstring).groups()
48294901
fcompresstype = ""
4830-
outlist = {'fnumfiles': fnumfiles, 'ffilestart': filestart, 'fformat': formversions[0], 'fcompression': fcompresstype, 'fencoding': fhencoding, 'fmtime': fheadmtime, 'fctime': fheadctime, 'fversion': formversions[1], 'fostype': fostype, 'fprojectname': fprojectname, 'fimptype': fpythontype, 'fheadersize': fheadsize, 'fsize': CatSizeEnd, 'fnumfields': fnumfields + 2, 'fformatspecs': formatspecs, 'fseeknextfile': fseeknextfile, 'fchecksumtype': fprechecksumtype, 'fheaderchecksum': fprechecksum, 'fjsonchecksumtype': fjsonchecksumtype, 'fjsontype': fjsontype, 'fjsonlen': fjsonlen, 'fjsonsize': fjsonsize, 'fjsonrawdata': fjsonrawcontent, 'fjsondata': fjsoncontent, 'fjstart': fjstart, 'fjend': fjend, 'fjsonchecksum': fjsonchecksum, 'frawheader': [formstring] + inheader, 'fextrafields': fnumextrafields, 'fextrafieldsize': fnumextrafieldsize, 'fextradata': fextrafieldslist, 'fvendorfields': fvendorfields, 'fvendordata': fvendorfieldslist, 'ffilelist': []}
4902+
outlist = {'fnumfiles': fnumfiles, 'ffilestart': filestart, 'fformat': formversions[0], 'fcompression': fcompresstype, 'fencoding': fhencoding, 'fmtime': fheadmtime, 'fctime': fheadctime, 'fversion': formversions[1], 'fostype': fostype, 'fprojectname': fprojectname, 'fimptype': fpythontype, 'fheadersize': fheadsize, 'fnumfields': fnumfields + 2, 'fformatspecs': formatspecs, 'fseeknextfile': fseeknextfile, 'fchecksumtype': fprechecksumtype, 'fheaderchecksum': fprechecksum, 'fjsonchecksumtype': fjsonchecksumtype, 'fjsontype': fjsontype, 'fjsonlen': fjsonlen, 'fjsonsize': fjsonsize, 'fjsonrawdata': fjsonrawcontent, 'fjsondata': fjsoncontent, 'fjstart': fjstart, 'fjend': fjend, 'fjsonchecksum': fjsonchecksum, 'frawheader': [formstring] + inheader, 'fextrafields': fnumextrafields, 'fextrafieldsize': fnumextrafieldsize, 'fextradata': fextrafieldslist, 'fvendorfields': fvendorfields, 'fvendordata': fvendorfieldslist, 'ffilelist': []}
48314903
if (seekstart < 0) or (seekstart > fnumfiles):
48324904
seekstart = 0
48334905
if (seekend == 0) or (seekend > fnumfiles) or (seekend < seekstart):
@@ -4910,15 +4982,17 @@ def ReadFileDataWithContentToArray(fp, filestart=0, seekstart=0, seekend=0, list
49104982
il = il + 1
49114983
realidnum = 0
49124984
countnum = seekstart
4913-
while (fp.tell() < CatSizeEnd) if seektoend else (countnum < seekend):
4985+
while (countnum < seekend):
49144986
HeaderOut = ReadFileHeaderDataWithContentToArray(fp, listonly, contentasfile, uncompress, skipchecksum, formatspecs, saltkey)
49154987
if(len(HeaderOut) == 0):
49164988
break
49174989
HeaderOut.update({'fid': realidnum, 'fidalt': realidnum})
49184990
outlist['ffilelist'].append(HeaderOut)
49194991
countnum = countnum + 1
49204992
realidnum = realidnum + 1
4921-
outlist.update({'fp': fp})
4993+
CatSize = fp.tell()
4994+
CatSizeEnd = CatSize
4995+
outlist.update({'fp': fp, 'fsize': CatSizeEnd})
49224996
return outlist
49234997

49244998

@@ -4927,12 +5001,6 @@ def ReadFileDataWithContentToList(fp, filestart=0, seekstart=0, seekend=0, listo
49275001
return False
49285002
delimiter = formatspecs['format_delimiter']
49295003
curloc = filestart
4930-
try:
4931-
fp.seek(0, 2)
4932-
except (OSError, ValueError):
4933-
SeekToEndOfFile(fp)
4934-
CatSize = fp.tell()
4935-
CatSizeEnd = CatSize
49365004
fp.seek(curloc, 0)
49375005
inheaderver = str(int(formatspecs['format_ver'].replace(".", "")))
49385006
headeroffset = fp.tell()
@@ -5180,13 +5248,15 @@ def ReadFileDataWithContentToList(fp, filestart=0, seekstart=0, seekend=0, listo
51805248
il = il + 1
51815249
realidnum = 0
51825250
countnum = seekstart
5183-
while (fp.tell() < CatSizeEnd) if seektoend else (countnum < seekend):
5251+
while (countnum < seekend):
51845252
HeaderOut = ReadFileHeaderDataWithContentToList(fp, listonly, contentasfile, uncompress, skipchecksum, formatspecs, saltkey)
51855253
if(len(HeaderOut) == 0):
51865254
break
51875255
outlist.append(HeaderOut)
51885256
countnum = countnum + 1
51895257
realidnum = realidnum + 1
5258+
CatSize = fp.tell()
5259+
CatSizeEnd = CatSize
51905260
return outlist
51915261

51925262
def ReadInFileWithContentToArray(infile, fmttype="auto", filestart=0, seekstart=0, seekend=0, listonly=False, contentasfile=True, uncompress=True, skipchecksum=False, formatspecs=__file_format_multi_dict__, saltkey=None, seektoend=False):
@@ -5268,17 +5338,11 @@ def ReadInFileWithContentToArray(infile, fmttype="auto", filestart=0, seekstart=
52685338
currentfilepos = readfp.tell()
52695339
else:
52705340
infp = UncompressFileAlt(readfp, formatspecs, currentfilepos)
5341+
if(not infp):
5342+
break
52715343
infp.seek(0, 0)
52725344
currentinfilepos = infp.tell()
5273-
try:
5274-
infp.seek(0, 2)
5275-
except (OSError, ValueError):
5276-
SeekToEndOfFile(infp)
5277-
outinfsize = infp.tell()
5278-
infp.seek(currentinfilepos, 0)
52795345
while True:
5280-
if currentinfilepos >= outinfsize: # stop when function signals False
5281-
break
52825346
oldinfppos = infp.tell()
52835347
compresscheck = CheckCompressionType(infp, formatspecs, currentinfilepos, False)
52845348
if(IsNestedDict(formatspecs) and compresscheck in formatspecs):
@@ -5385,17 +5449,11 @@ def ReadInFileWithContentToList(infile, fmttype="auto", filestart=0, seekstart=0
53855449
currentfilepos = readfp.tell()
53865450
else:
53875451
infp = UncompressFileAlt(readfp, formatspecs, currentfilepos)
5452+
if(not infp):
5453+
break
53885454
infp.seek(0, 0)
53895455
currentinfilepos = infp.tell()
5390-
try:
5391-
infp.seek(0, 2)
5392-
except (OSError, ValueError):
5393-
SeekToEndOfFile(infp)
5394-
outinfsize = infp.tell()
5395-
infp.seek(currentinfilepos, 0)
53965456
while True:
5397-
if currentinfilepos >= outinfsize: # stop when function signals False
5398-
break
53995457
oldinfppos = infp.tell()
54005458
compresscheck = CheckCompressionType(infp, formatspecs, currentinfilepos, False)
54015459
if(IsNestedDict(formatspecs) and compresscheck in formatspecs):
@@ -8013,14 +8071,6 @@ def NeoFileValidate(infile, fmttype="auto", filestart=0, formatspecs=__file_form
80138071
return False
80148072
fp = UncompressFile(infile, formatspecs, "rb", filestart)
80158073

8016-
try:
8017-
fp.seek(0, 2)
8018-
except (OSError, ValueError):
8019-
SeekToEndOfFile(fp)
8020-
CatSize = fp.tell()
8021-
CatSizeEnd = CatSize
8022-
fp.seek(0)
8023-
fp.seek(curloc, 0)
80248074
if(IsNestedDict(formatspecs)):
80258075
compresschecking = CheckCompressionType(fp, formatspecs, filestart, False)
80268076
if(compresschecking not in formatspecs):
@@ -8117,7 +8167,7 @@ def NeoFileValidate(infile, fmttype="auto", filestart=0, formatspecs=__file_form
81178167
if(verbose):
81188168
VerbosePrintOut("")
81198169
# Iterate either until EOF (seektoend) or fixed count
8120-
while (fp.tell() < CatSizeEnd) if seektoend else (il < fnumfiles):
8170+
while (il < fnumfiles):
81218171
outfhstart = fp.tell()
81228172
if(__use_new_style__):
81238173
inheaderdata = ReadFileHeaderDataBySize(fp, formatspecs['format_delimiter'])

0 commit comments

Comments
 (0)