Compare commits

...

14 Commits

Author SHA1 Message Date
Safihre
3bf9906f45 Update text files for 3.2.1RC1
draft release
2021-03-18 10:30:05 +01:00
Safihre
9f7daf96ef Update URL for Python 3 information 2021-03-18 09:10:39 +01:00
Sander
67de4df155 deobfuscate: no globber, but use given filelist (#1830) 2021-03-18 09:10:31 +01:00
Safihre
bc51a4bd1c Remove old compatibility code from BPSMeter that causes crash on startup
Closes #1827
2021-03-18 09:10:23 +01:00
Sander
bb54616018 deobfuscate: rename accompanying (smaller) files with same basename, and no renaming of collections with same extension (#1826)
* deobfuscate: rename accompanying (smaller) files with same basename

* deobfuscate: do not rename collections of same extension

* deobfuscate: collection ... much easier with one loop, thanks safihre.

* deobfuscate: globber_full, and cleanup

* deobfuscate: unittest test_deobfuscate_big_file_small_accompanying_files

* deobfuscate: unittest test_deobfuscate_collection_with_same_extension

* deobfuscate: unittest test_deobfuscate_collection_with_same_extension
2021-03-18 09:10:18 +01:00
Safihre
6bcff5e014 More space for the RSS table
Closes #1824
2021-03-18 09:10:09 +01:00
puzzledsab
8970a03a9a Use binary mode to make write test more accurate on Windows (#1815) 2021-03-10 22:23:10 +01:00
Safihre
3ad717ca35 Single indexer categories would be saved with "," between each letter 2021-03-10 22:23:10 +01:00
jcfp
b14f72c67a fix config auto_sort setting, broken by #1666 (#1813)
* fix config auto_sort setting, broken by #1666

* oops I did it again
2021-03-10 22:23:10 +01:00
Safihre
45d036804f Show name of item to be deleted from queue/history in confirm dialog 2021-03-10 22:23:10 +01:00
Safihre
8f606db233 Add traceback when failing to read the password file
Closes #1810
2021-03-10 22:23:10 +01:00
Safihre
3766ba5402 pre-create subdir if needed (POSIX, par2) (#1802)
* pre-create subdir it needed

* pre-create subdir it needed: check if already exists

* use os.makedirs() to handle subdir1/subdir2/blabla

* protect against malicous "..", and better naming

* check for Windows \ and POSIX /

* check again within path, typo and formatting

* regex: square brackets

* cleanup: only "/" can occur in par2

* cleanup: better logging

* unit test: testing of filesystem.renamer()

* if subdir specified in par2: let filesystem.renamer() do all the work

* if create_local_directories=True, then renamer() must stay within specified directory. Plus unittest for that.

* if create_local_directories=True, then renamer() must stay within specified directory. Plus unittest for that.

* more comments in code

* use filesystem.create_all_dirs(), less logging, clearer "..", and other feedback from Safihre

* make remote black happy too

* Small changes in wording of comments and error

Co-authored-by: Safihre <safihre@sabnzbd.org>
2021-03-10 22:23:10 +01:00
jxyzn
e851813cef Sanitize names possibly derived from X-DNZB-EpisodeName (#1806) 2021-03-10 22:15:23 +01:00
thezoggy
4d49ad9141 3.2.x cleanup (#1808)
* Update uni_config bootstrap css to same version of js (3.3.7).
* small accessibility change, removed thin dot border on focus

* Ignore VS Code settings folder

* cherry picked 'Fix disabled select for Glitter Night'

* glitter night - fix search border color
2021-02-27 14:47:44 +01:00
22 changed files with 320 additions and 46 deletions

3
.gitignore vendored
View File

@@ -31,6 +31,9 @@ SABnzbd-*/
*.wp[ru]
.idea
# VScode
.vscode/
# Testing folders
.cache
.xprocess

View File

@@ -1,7 +1,7 @@
Metadata-Version: 1.0
Name: SABnzbd
Version: 3.2.0
Summary: SABnzbd-3.2.0
Version: 3.2.1RC1
Summary: SABnzbd-3.2.1RC1
Home-page: https://sabnzbd.org
Author: The SABnzbd Team
Author-email: team@sabnzbd.org

View File

@@ -1,6 +1,21 @@
Release Notes - SABnzbd 3.2.0
Release Notes - SABnzbd 3.2.1 Release Candidate 1
=========================================================
## Changes and bugfixes since 3.2.0
- Single `Indexer Categories` in Categories were broken.
- Program would fail to start if Quota was previously exceeded.
- Setting `Automatically sort queue` by `Age` was inverted.
- Show the name of the item to be deleted from the Queue/History
in the confirmation dialog.
- Handle directories in `.par2`-files during Quick-check.
- Improvements to `Deobfuscate final filenames`:
Rename accompanying (smaller) files with the same basename.
Do not rename collections of the same extension.
- Sanitize names possibly derived from `X-DNZB-EpisodeName`.
- Widened the RSS feeds table.
- Add traceback-logging when failing to read the password file.
- Windows: Use binary mode to make the write test more accurate.
## Changes since 3.1.1
- Python 3.6 is the minimum required version.
- The Windows installer can only be used on 64bit Windows 8.1 and

View File

@@ -19,7 +19,7 @@ import sys
if sys.hexversion < 0x03060000:
print("Sorry, requires Python 3.6 or above")
print("You can read more at: https://sabnzbd.org/python3")
print("You can read more at: https://sabnzbd.org/wiki/installation/install-off-modules")
sys.exit(1)
import logging
@@ -48,7 +48,7 @@ try:
except ImportError as e:
print("Not all required Python modules are available, please check requirements.txt")
print("Missing module:", e.name)
print("You can read more at: https://sabnzbd.org/python3")
print("You can read more at: https://sabnzbd.org/wiki/installation/install-off-modules")
print("If you still experience problems, remove all .pyc files in this folder and subfolders")
sys.exit(1)
@@ -68,7 +68,6 @@ from sabnzbd.misc import (
get_serv_parms,
get_from_url,
upload_file_to_sabnzbd,
is_ipv4_addr,
is_localhost,
is_lan_addr,
)

View File

@@ -10,7 +10,7 @@
<p>$T('explain-RSS')</p>
<form action="add_rss_feed" method="post" autocomplete="off">
<input type="hidden" name="apikey" value="$apikey" />
<table class="catTable">
<table class="catTable addRssTable">
<tr>
<th>&nbsp;</th>
<th>$T('name')</th>
@@ -21,10 +21,10 @@
<td>
<input type="checkbox" name="enable" value="1" checked />
</td>
<td>
<input type="text" name="feed" class="smaller_input" value="$feed" />
<td class="new-feed-title">
<input type="text" name="feed" value="$feed" />
</td>
<td>
<td class="new-feed-url">
<input type="text" name="uri" placeholder="$T('addMultipleFeeds')" />
</td>
<td class="nowrap">

View File

@@ -136,8 +136,8 @@
<label class="config" for="auto_sort">$T('opt-auto_sort')</label>
<select name="auto_sort" id="auto_sort">
<option value="">$T('default')</option>
<option value="avg_age asc" <!--#if $auto_sort == "avg_age asc" then 'selected="selected"' else ""#--> >$T('Glitter-sortAgeAsc')</option>
<option value="avg_age desc" <!--#if $auto_sort == "avg_age desc" then 'selected="selected"' else ""#--> >$T('Glitter-sortAgeDesc')</option>
<option value="avg_age desc" <!--#if $auto_sort == "avg_age desc" then 'selected="selected"' else ""#--> >$T('Glitter-sortAgeAsc')</option>
<option value="avg_age asc" <!--#if $auto_sort == "avg_age asc" then 'selected="selected"' else ""#--> >$T('Glitter-sortAgeDesc')</option>
<option value="name asc" <!--#if $auto_sort == "name asc" then 'selected="selected"' else ""#--> >$T('Glitter-sortNameAsc')</option>
<option value="name desc" <!--#if $auto_sort == "name desc" then 'selected="selected"' else ""#--> >$T('Glitter-sortNameDesc')</option>
<option value="size asc" <!--#if $auto_sort == "size asc" then 'selected="selected"' else ""#--> >$T('Glitter-sortSizeAsc')</option>

View File

File diff suppressed because one or more lines are too long

View File

@@ -551,6 +551,16 @@ tr.separator {
padding-right: 13px;
}
/* -- */
.RSS .addRssTable,
.RSS .addRssTable input[type="text"] {
width: 100%;
}
.RSS .addRssTable .new-feed-title {
max-width: 250px;
}
.RSS .addRssTable .new-feed-url {
width: 70%;
}
h2.activeRSS {
margin-bottom: 10px;
}
@@ -560,12 +570,12 @@ h2.activeRSS {
text-decoration: underline !important;
}
.favicon {
background-position: center center!important;
background-size: 16px 16px;
background-position: center center !important;
background-size: 22px 22px;
opacity: 1;
top: -1px;
height: 16px;
width: 16px;
height: 22px;
width: 22px;
float: left;
margin: 0 6px 0 2px;
text-align: center;
@@ -585,6 +595,7 @@ h2.activeRSS {
}
#subscriptions {
border: 1px solid #E5E5E5;
width: 100%;
}
.data-row {
border-top: 1px solid #E5E5E5;
@@ -596,6 +607,7 @@ h2.activeRSS {
#subscriptions .chk {
padding: 8px 5px 5px;
vertical-align: middle;
width: 40px;
}
#subscriptions .title {
font-weight: bold;
@@ -603,10 +615,11 @@ h2.activeRSS {
width: auto;
}
#subscriptions .favicon {
margin-left: 8px;
margin-left: 7px;
margin-top: -2px;
}
.ie6 .subscription-title {
width: 20em;
#subscriptions .glyphicon {
margin-top: 3px;
}
.subscription-title,
.subscription-title:hover {

View File

@@ -59,6 +59,7 @@
glitterTranslate.shutdown = "$T('shutdownOK?')";
glitterTranslate.restart = "$T('explain-Restart') $T('explain-needNewLogin')".replace(/\<br(\s*\/|)\>/g, '\n');
glitterTranslate.repair = "$T('explain-Repair')".replace(/<br \/>/g, "\n").replace(/&quot;/g,'"');
glitterTranslate.deleteMsg = "$T('nzo-delete')";
glitterTranslate.removeDown = "$T('Glitter-confirmClearDownloads')";
glitterTranslate.removeDow1 = "$T('Glitter-confirmClear1Download')";
glitterTranslate.retryAll = "$T('link-retryAll')?";

View File

@@ -421,7 +421,7 @@ function HistoryModel(parent, data) {
// Delete button
self.deleteSlot = function(item, event) {
// Confirm?
if(!self.parent.parent.confirmDeleteHistory() || confirm(glitterTranslate.removeDow1)) {
if(!self.parent.parent.confirmDeleteHistory() || confirm(glitterTranslate.deleteMsg + ":\n" + item.historyStatus.name() + "\n\n" + glitterTranslate.removeDow1)) {
// Are we still processing and it can be stopped?
if(item.processingDownload() == 2) {
callAPI({

View File

@@ -724,7 +724,7 @@ function QueueModel(parent, data) {
// Remove 1 download from queue
self.removeDownload = function(item, event) {
// Confirm and remove
if(!self.parent.parent.confirmDeleteQueue() || confirm(glitterTranslate.removeDow1)) {
if(!self.parent.parent.confirmDeleteQueue() || confirm(glitterTranslate.deleteMsg + ":\n" + item.name() + "\n\n" + glitterTranslate.removeDow1)) {
var itemToDelete = this;
// Show notification

View File

@@ -55,6 +55,10 @@ legend,
opacity: 0.7;
}
.form-control[disabled] {
opacity: 0.65;
}
.progress {
background-color: #DADADA;
}
@@ -126,6 +130,10 @@ select.form-control,
.main-content .btn-default,
.modal-body .btn-default,
.modal-footer .btn-default,
.btn-default.disabled:hover,
.btn-default.disabled:active,
.btn-default.disabled:focus,
.form-control[disabled],
#modal-options .options-function-box .input-group-addon {
background-color: #555555;
color: #EBEBEB;
@@ -157,6 +165,8 @@ tbody>tr:last-child td,
input,
input.form-control,
.input-group-addon,
.search-box input:focus,
.search-box input:valid,
select.form-control,
#modal-options .table-server-connections th,
.main-content .btn-default,

View File

@@ -195,13 +195,6 @@ class BPSMeter:
res = self.reset_quota()
except:
self.defaults()
# Force update of counters and validate data
try:
for server in self.grand_total.keys():
self.update(server)
except TypeError:
self.defaults()
self.update()
return res
def update(self, server: Optional[str] = None, amount: int = 0):

View File

@@ -1137,7 +1137,7 @@ def validate_single_tag(value):
"""
if len(value) == 3:
if value[1] == ">":
return None, " ".join(value)
return None, [" ".join(value)]
return None, value

View File

@@ -127,14 +127,13 @@ def is_probably_obfuscated(myinputfilename):
def deobfuscate_list(filelist, usefulname):
""" Check all files in filelist, and if wanted, deobfuscate """
""" Check all files in filelist, and if wanted, deobfuscate: rename to filename based on usefulname"""
# to be sure, only keep really exsiting files:
filelist = [f for f in filelist if os.path.exists(f)]
# Search for par2 files in the filelist
par2_files = [f for f in filelist if f.endswith(".par2")]
# Found any par2 files we can use?
run_renamer = True
if not par2_files:
@@ -152,22 +151,57 @@ def deobfuscate_list(filelist, usefulname):
# No par2 files? Then we try to rename qualifying (big, not-excluded, obfuscated) files to the job-name
if run_renamer:
excluded_file_exts = EXCLUDED_FILE_EXTS
# If there is a collection with bigger files with the same extension, we don't want to rename it
extcounter = {}
for file in filelist:
if os.path.getsize(file) < MIN_FILE_SIZE:
# too small to care
continue
_, ext = os.path.splitext(file)
if ext in extcounter:
extcounter[ext] += 1
else:
extcounter[ext] = 1
if extcounter[ext] >= 3 and ext not in excluded_file_exts:
# collection, and extension not yet in excluded_file_exts, so add it
excluded_file_exts = (*excluded_file_exts, ext)
logging.debug(
"Found a collection of at least %s files with extension %s, so not renaming those files",
extcounter[ext],
ext,
)
logging.debug("Trying to see if there are qualifying files to be deobfuscated")
# We start with he biggest file ... probably the most important file
filelist = sorted(filelist, key=os.path.getsize, reverse=True)
for filename in filelist:
# check that file is still there (and not renamed by the secondary renaming process below)
if not os.path.isfile(filename):
continue
logging.debug("Deobfuscate inspecting %s", filename)
file_size = os.path.getsize(filename)
# Do we need to rename this file?
# Criteria: big, not-excluded extension, obfuscated (in that order)
if (
file_size > MIN_FILE_SIZE
and get_ext(filename) not in EXCLUDED_FILE_EXTS
os.path.getsize(filename) > MIN_FILE_SIZE
and get_ext(filename) not in excluded_file_exts
and is_probably_obfuscated(filename) # this as last test to avoid unnecessary analysis
):
# OK, rename
# Rename and make sure the new filename is unique
path, file = os.path.split(filename)
# construct new_name: <path><usefulname><extension>
new_name = get_unique_filename("%s%s" % (os.path.join(path, usefulname), get_ext(filename)))
logging.info("Deobfuscate renaming %s to %s", filename, new_name)
# Rename and make sure the new filename is unique
renamer(filename, new_name)
# find other files with the same basename in filelist, and rename them in the same way:
basedirfile, _ = os.path.splitext(filename) # something like "/home/this/myiso"
for otherfile in filelist:
if otherfile.startswith(basedirfile + ".") and os.path.isfile(otherfile):
# yes, same basedirfile, only different extension
remainingextension = otherfile.replace(basedirfile, "") # might be long ext, like ".dut.srt"
new_name = get_unique_filename("%s%s" % (os.path.join(path, usefulname), remainingextension))
logging.info("Deobfuscate renaming %s to %s", otherfile, new_name)
# Rename and make sure the new filename is unique
renamer(otherfile, new_name)
else:
logging.info("No qualifying files found to deobfuscate")

View File

@@ -806,8 +806,9 @@ def get_filepath(path: str, nzo, filename: str):
@synchronized(DIR_LOCK)
def renamer(old: str, new: str):
""" Rename file/folder with retries for Win32 """
def renamer(old: str, new: str, create_local_directories: bool = False):
"""Rename file/folder with retries for Win32
Optionally alows the creation of local directories if they don't exist yet"""
# Sanitize last part of new name
path, name = os.path.split(new)
new = os.path.join(path, sanitize_filename(name))
@@ -816,6 +817,19 @@ def renamer(old: str, new: str):
if old == new:
return
# In case we want nonexistent directories to be created, check for directory escape (forbidden)
if create_local_directories:
oldpath, _ = os.path.split(old)
# Check not outside directory
# In case of "same_file() == 1": same directory, so nothing to do
if same_file(oldpath, path) == 0:
# Outside current directory, this is most likely malicious
logging.error(T("Blocked attempt to create directory %s"), path)
raise OSError("Refusing to go outside directory")
elif same_file(oldpath, path) == 2:
# Sub-directory, so create if does not yet exist:
create_all_dirs(path)
logging.debug('Renaming "%s" to "%s"', old, new)
if sabnzbd.WIN32:
retries = 10

View File

@@ -808,6 +808,7 @@ def get_all_passwords(nzo):
)
except:
logging.warning(T("Failed to read the password file %s"), pw_file)
logging.info("Traceback: ", exc_info=True)
if nzo.password:
# If an explicit password was set, add a retry without password, just in case.

View File

@@ -55,6 +55,7 @@ from sabnzbd.filesystem import (
setname_from_path,
get_ext,
get_filename,
same_file,
)
from sabnzbd.nzbstuff import NzbObject, NzbFile
from sabnzbd.sorting import SeriesSorter
@@ -2077,7 +2078,14 @@ def quick_check_set(set, nzo):
if nzf.md5sum == md5pack[file]:
try:
logging.debug("Quick-check will rename %s to %s", nzf.filename, file)
renamer(os.path.join(nzo.download_path, nzf.filename), os.path.join(nzo.download_path, file))
# Note: file can and is allowed to be in a subdirectory.
# Subdirectories in par2 always contain "/", not "\"
renamer(
os.path.join(nzo.download_path, nzf.filename),
os.path.join(nzo.download_path, file),
create_local_directories=True,
)
renames[file] = nzf.filename
nzf.filename = file
result &= True

View File

@@ -35,6 +35,7 @@ from sabnzbd.filesystem import (
get_unique_filename,
get_ext,
renamer,
sanitize_and_trim_path,
sanitize_foldername,
clip_path,
)
@@ -492,6 +493,7 @@ class SeriesSorter:
newpath = os.path.join(current_path, newname)
# Replace %ext with extension
newpath = newpath.replace("%ext", self.ext)
newpath = sanitize_and_trim_path(newpath)
try:
logging.debug("Rename: %s to %s", filepath, newpath)
renamer(filepath, newpath)

View File

@@ -22,7 +22,10 @@ def diskspeedmeasure(my_dirname: str) -> float:
try:
# Use low-level I/O
fp_testfile = os.open(filename, os.O_CREAT | os.O_WRONLY, 0o777)
try:
fp_testfile = os.open(filename, os.O_CREAT | os.O_WRONLY | os.O_BINARY, 0o777)
except AttributeError:
fp_testfile = os.open(filename, os.O_CREAT | os.O_WRONLY, 0o777)
# Start looping
total_time = 0.0

View File

@@ -32,6 +32,11 @@ def create_big_file(filename):
myfile.truncate(15 * 1024 * 1024)
def create_small_file(filename):
with open(filename, "wb") as myfile:
myfile.truncate(1024)
class TestDeobfuscateFinalResult:
def test_is_probably_obfuscated(self):
# Test the base function test_is_probably_obfuscated(), which gives a boolean as RC
@@ -178,6 +183,107 @@ class TestDeobfuscateFinalResult:
# Done. Remove (non-empty) directory
shutil.rmtree(dirname)
def test_deobfuscate_big_file_small_accompanying_files(self):
# input: myiso.iso, with accompanying files (.srt files in this case)
# test that the small accompanying files (with same basename) are renamed accordingly to the big ISO
# Create directory (with a random directory name)
dirname = os.path.join(SAB_DATA_DIR, "testdir" + str(random.randint(10000, 99999)))
os.mkdir(dirname)
# Create a big enough file with a non-useful filename
isofile = os.path.join(dirname, "myiso.iso")
create_big_file(isofile)
assert os.path.isfile(isofile)
# and a srt file
srtfile = os.path.join(dirname, "myiso.srt")
create_small_file(srtfile)
assert os.path.isfile(srtfile)
# and a dut.srt file
dutsrtfile = os.path.join(dirname, "myiso.dut.srt")
create_small_file(dutsrtfile)
assert os.path.isfile(dutsrtfile)
# and a non-related file
txtfile = os.path.join(dirname, "something.txt")
create_small_file(txtfile)
assert os.path.isfile(txtfile)
# create the filelist, with just the above files
myfilelist = [isofile, srtfile, dutsrtfile, txtfile]
# and now unleash the magic on that filelist, with a more useful jobname:
jobname = "My Important Download 2020"
deobfuscate_list(myfilelist, jobname)
# Check original files:
assert not os.path.isfile(isofile) # original iso not be there anymore
assert not os.path.isfile(srtfile) # ... and accompanying file neither
assert not os.path.isfile(dutsrtfile) # ... and this one neither
assert os.path.isfile(txtfile) # should still be there: not accompanying, and too small to rename
# Check the renaming
assert os.path.isfile(os.path.join(dirname, jobname + ".iso")) # ... should be renamed to the jobname
assert os.path.isfile(os.path.join(dirname, jobname + ".srt")) # ... should be renamed to the jobname
assert os.path.isfile(os.path.join(dirname, jobname + ".dut.srt")) # ... should be renamed to the jobname
# Done. Remove (non-empty) directory
shutil.rmtree(dirname)
def test_deobfuscate_collection_with_same_extension(self):
# input: a collection of bigger files with the same extension
# test that there is no renaming on the collection ... as that's useless on a collection
# Create directory (with a random directory name)
dirname = os.path.join(SAB_DATA_DIR, "testdir" + str(random.randint(10000, 99999)))
os.mkdir(dirname)
# Create big enough files with a non-useful filenames, all with same extension
file1 = os.path.join(dirname, "file1.bla")
create_big_file(file1)
assert os.path.isfile(file1)
file2 = os.path.join(dirname, "file2.bla")
create_big_file(file2)
assert os.path.isfile(file2)
file3 = os.path.join(dirname, "file3.bla")
create_big_file(file3)
assert os.path.isfile(file3)
file4 = os.path.join(dirname, "file4.bla")
create_big_file(file4)
assert os.path.isfile(file4)
# other extension ... so this one should get renamed
otherfile = os.path.join(dirname, "other.bin")
create_big_file(otherfile)
assert os.path.isfile(otherfile)
# create the filelist, with the above files
myfilelist = [file1, file2, file3, file4, otherfile]
# and now unleash the magic on that filelist, with a more useful jobname:
jobname = "My Important Download 2020"
deobfuscate_list(myfilelist, jobname)
# Check original files:
# the collection with same extension should still be there:
assert os.path.isfile(file1) # still there
assert os.path.isfile(file2) # still there
assert os.path.isfile(file3) # still there
assert os.path.isfile(file4) # still there
# but the one separate file with obfuscated name should be renamed:
assert not os.path.isfile(otherfile) # should be renamed
# Check the renaming
assert os.path.isfile(os.path.join(dirname, jobname + ".bin")) # ... should be renamed to the jobname
# Done. Remove (non-empty) directory
shutil.rmtree(dirname)
def test_deobfuscate_filelist_nasty_tests(self):
# check no problems occur with nasty use cases

View File

@@ -21,6 +21,9 @@ tests.test_filesystem - Testing functions in filesystem.py
import stat
import sys
import os
import random
import shutil
from pathlib import Path
import pyfakefs.fake_filesystem_unittest as ffs
@@ -983,3 +986,73 @@ class TestSetPermissions(ffs.TestCase, PermissionCheckerHelper):
def test_dir1755_umask4755_setting(self):
# Sticky bit on directory, umask with setuid
self._runner("1755", "4755")
class TestRenamer:
# test filesystem.renamer() for different scenario's
def test_renamer(self):
# First of all, create a working directory (with a random name)
dirname = os.path.join(SAB_DATA_DIR, "testdir" + str(random.randint(10000, 99999)))
os.mkdir(dirname)
# base case: rename file within directory
filename = os.path.join(dirname, "myfile.txt")
Path(filename).touch() # create file
newfilename = os.path.join(dirname, "newfile.txt")
filesystem.renamer(filename, newfilename) # rename() does not return a value ...
assert not os.path.isfile(filename)
assert os.path.isfile(newfilename)
# standard behaviour: renaming (moving) into an exiting other directory *is* allowed
filename = os.path.join(dirname, "myfile.txt")
Path(filename).touch() # create file
sameleveldirname = os.path.join(SAB_DATA_DIR, "othertestdir" + str(random.randint(10000, 99999)))
os.mkdir(sameleveldirname)
newfilename = os.path.join(sameleveldirname, "newfile.txt")
filesystem.renamer(filename, newfilename)
assert not os.path.isfile(filename)
assert os.path.isfile(newfilename)
shutil.rmtree(sameleveldirname)
# Default: renaming into a non-existing subdirectory not allowed
Path(filename).touch() # create file
newfilename = os.path.join(dirname, "nonexistingsubdir", "newfile.txt")
try:
filesystem.renamer(filename, newfilename) # rename() does not return a value ...
except:
pass
assert os.path.isfile(filename)
assert not os.path.isfile(newfilename)
# Creation of subdirectory is allowed if create_local_directories=True
Path(filename).touch()
newfilename = os.path.join(dirname, "newsubdir", "newfile.txt")
try:
filesystem.renamer(filename, newfilename, create_local_directories=True)
except:
pass
assert not os.path.isfile(filename)
assert os.path.isfile(newfilename)
# Creation of subdirectory plus deeper sudbdir is allowed if create_local_directories=True
Path(filename).touch()
newfilename = os.path.join(dirname, "newsubdir", "deepersubdir", "newfile.txt")
try:
filesystem.renamer(filename, newfilename, create_local_directories=True)
except:
pass
assert not os.path.isfile(filename)
assert os.path.isfile(newfilename)
# ... escaping the directory plus subdir creation is not allowed
Path(filename).touch()
newfilename = os.path.join(dirname, "..", "newsubdir", "newfile.txt")
try:
filesystem.renamer(filename, newfilename, create_local_directories=True)
except:
pass
assert os.path.isfile(filename)
assert not os.path.isfile(newfilename)
# Cleanup working directory
shutil.rmtree(dirname)