Skip to content

Commit

Permalink
Added system exit codes (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
pcstout authored Jul 28, 2021
1 parent 9f9561f commit f91dfb9
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 86 deletions.
1 change: 1 addition & 0 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ environment:

install:
- set PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%
- pip install --upgrade pip
- pip install pipenv
- pipenv --python=%PYTHON%\\python.exe
- pipenv lock -r >> requirements.txt
Expand Down
16 changes: 14 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
# Change Log

## Version 0.0.5 (2021-07-28)

### Added

- Added system exit codes.

## Version 0.0.4 (2020-10-09)

### Added

- Refactor force upload.
- Added `--cache-dir` flag.

## Version 0.0.3 (2020-09-21)

### Added

- Added `--force-upload` flag.
- Added `--version` flag.


## Version 0.0.2 (2020-06-17)

### Added
- Upgraded synapseclient to version 2.1.0

- Upgraded synapseclient to version 2.1.0

## Version 0.0.1 (2019-11-20)

### Added

- Initial release.
2 changes: 1 addition & 1 deletion src/synapse_uploader/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.0.4'
__version__ = '0.0.5'
41 changes: 28 additions & 13 deletions src/synapse_uploader/cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import sys
import logging
import argparse
from datetime import datetime
Expand Down Expand Up @@ -102,19 +103,33 @@ def main():

print('Logging output to: {0}'.format(log_filename))

SynapseUploader(
args.entity_id,
args.local_path,
remote_path=args.remote_folder_path,
max_depth=args.depth,
max_threads=args.threads,
username=args.username,
password=args.password,
force_upload=args.force_upload,
cache_dir=args.cache_dir
).execute()

print('Output logged to: {0}'.format(log_filename))
try:
cmd = SynapseUploader(
args.entity_id,
args.local_path,
remote_path=args.remote_folder_path,
max_depth=args.depth,
max_threads=args.threads,
username=args.username,
password=args.password,
force_upload=args.force_upload,
cache_dir=args.cache_dir
)
cmd.execute()
if cmd.errors:
logging.error('Finished with errors.')
for error in cmd.errors:
print(error)
print('Output logged to: {0}'.format(log_filename))
sys.exit(1)
else:
logging.info('Finished successfully.')
print('Output logged to: {0}'.format(log_filename))
sys.exit(0)
except Exception as ex:
logging.error(ex)
print('Output logged to: {0}'.format(log_filename))
sys.exit(1)


if __name__ == "__main__":
Expand Down
69 changes: 34 additions & 35 deletions src/synapse_uploader/synapse_uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,7 @@ def __init__(self,

self._thread_lock = threading.Lock()
self._synapse_parents = {}
self.has_errors = False

if max_depth > self.MAX_SYNAPSE_DEPTH:
raise Exception('Maximum depth must be less than or equal to {0}.'.format(self.MAX_SYNAPSE_DEPTH))

if max_depth < self.MIN_SYNAPSE_DEPTH:
raise Exception('Maximum depth must be greater than or equal to {0}.'.format(self.MIN_SYNAPSE_DEPTH))
self.errors = []

if remote_path:
self._remote_path = remote_path.replace(' ', '').lstrip(os.sep).rstrip(os.sep)
Expand All @@ -62,13 +56,20 @@ def __init__(self,
def execute(self):
self.start_time = datetime.now()

if self._max_depth > self.MAX_SYNAPSE_DEPTH:
self._show_error('Maximum depth must be less than or equal to {0}.'.format(self.MAX_SYNAPSE_DEPTH))
return self

if self._max_depth < self.MIN_SYNAPSE_DEPTH:
self._show_error('Maximum depth must be greater than or equal to {0}.'.format(self.MIN_SYNAPSE_DEPTH))
return self

if not self._synapse_login():
self.has_errors = True
logging.error('Could not log into Synapse. Aborting.')
return
self._show_error('Could not log into Synapse. Aborting.')
return self

if self._force_upload:
print('Forcing upload. Entity versions will be incremented.')
logging.info('Forcing upload. Entity versions will be incremented.')

remote_entity = self._synapse_client.get(self._synapse_entity_id, downloadFile=False)
remote_entity_is_file = False
Expand All @@ -83,7 +84,8 @@ def execute(self):
remote_type = 'File'
remote_entity_is_file = True
else:
raise Exception('Remote entity must be a project, folder, or file. Found {0}'.format(type(remote_entity)))
self._show_error('Remote entity must be a project, folder, or file. Found {0}'.format(type(remote_entity)))
return self

local_entity_is_file = False

Expand All @@ -93,14 +95,16 @@ def execute(self):
elif os.path.isdir(self._local_path):
local_type = 'Directory'
else:
raise Exception('Local entity must be a directory or file: {0}'.format(self._local_path))
self._show_error('Local entity must be a directory or file: {0}'.format(self._local_path))
return self

if remote_entity_is_file and not local_entity_is_file:
raise Exception('Local entity must be a file when remote entity is a file: {0}'.format(self._local_path))
self._show_error('Local entity must be a file when remote entity is a file: {0}'.format(self._local_path))
return self

if remote_entity_is_file and self._remote_path:
raise Exception(
'Cannot specify a remote path when remote entity is a file: {0}'.format(self._local_path))
self._show_error('Cannot specify a remote path when remote entity is a file: {0}'.format(self._local_path))
return self

logging.info('Uploading to {0}: {1} ({2})'.format(remote_type, remote_entity.name, remote_entity.id))
logging.info('Uploading {0}: {1}'.format(local_type, self._local_path))
Expand All @@ -109,8 +113,9 @@ def execute(self):
remote_file_name = remote_entity['_file_handle']['fileName']
local_file_name = os.path.basename(self._local_path)
if local_file_name != remote_file_name:
raise Exception('Local filename: {0} does not match remote file name: {1}'.format(local_file_name,
remote_file_name))
self._show_error('Local filename: {0} does not match remote file name: {1}'.format(local_file_name,
remote_file_name))
return self

remote_parent = self._synapse_client.get(remote_entity.get('parentId'))
self._set_synapse_parent(remote_parent)
Expand All @@ -134,11 +139,7 @@ def execute(self):
self.end_time = datetime.now()
logging.info('')
logging.info('Run time: {0}'.format(self.end_time - self.start_time))

if self.has_errors:
logging.error('Finished with errors. Please see log file.')
else:
logging.info('Finished successfully.')
return self

def _synapse_login(self):
if self._synapse_client and self._synapse_client.credentials:
Expand All @@ -161,15 +162,13 @@ def _synapse_login(self):
self._synapse_client.login(self._username, self._password, silent=True)
except Exception as ex:
self._synapse_client = None
self.has_errors = True
logging.error('Synapse login failed: {0}'.format(str(ex)))
self._show_error('Synapse login failed: {0}'.format(str(ex)))

return self._synapse_client is not None

def _upload_folder(self, executor, local_path, synapse_parent):
if not synapse_parent:
self.has_errors = True
logging.error('Parent not found, cannot execute folder: {0}'.format(local_path))
self._show_error('Parent not found, cannot execute folder: {0}'.format(local_path))
return

parent = synapse_parent
Expand Down Expand Up @@ -201,8 +200,7 @@ def _create_folder_in_synapse(self, path, synapse_parent):
synapse_folder = None

if not synapse_parent:
self.has_errors = True
logging.error('Parent not found, cannot create folder: {0}'.format(path))
self._show_error('Parent not found, cannot create folder: {0}'.format(path))
return synapse_folder

folder_name = os.path.basename(path)
Expand All @@ -227,8 +225,7 @@ def _create_folder_in_synapse(self, path, synapse_parent):
time.sleep(sleep_time)

if exception:
self.has_errors = True
logging.error('[Folder FAILED] {0} -> {1} : {2}'.format(path, full_synapse_path, str(exception)))
self._show_error('[Folder FAILED] {0} -> {1} : {2}'.format(path, full_synapse_path, str(exception)))
else:
logging.info('[Folder] {0} -> {1}'.format(path, full_synapse_path))
self._set_synapse_parent(synapse_folder)
Expand All @@ -239,8 +236,7 @@ def _upload_file_to_synapse(self, local_file, synapse_parent):
synapse_file = None

if not synapse_parent:
self.has_errors = True
logging.error('Parent not found, cannot execute file: {0}'.format(local_file))
self._show_error('Parent not found, cannot execute file: {0}'.format(local_file))
return synapse_file

# Skip empty files since these will error when uploading via the synapseclient.
Expand Down Expand Up @@ -287,8 +283,7 @@ def _upload_file_to_synapse(self, local_file, synapse_parent):
time.sleep(sleep_time)

if exception:
self.has_errors = True
logging.error('[File FAILED] {0} -> {1} : {2}'.format(local_file, full_synapse_path, str(exception)))
self._show_error('[File FAILED] {0} -> {1} : {2}'.format(local_file, full_synapse_path, str(exception)))
else:
logging.info('[{0}] {1} -> {2}'.format(log_success_prefix, local_file, full_synapse_path))

Expand Down Expand Up @@ -359,3 +354,7 @@ def _get_dirs_and_files(self, local_path):
files.sort(key=lambda f: f.name)

return dirs, files

def _show_error(self, msg):
self.errors.append(msg)
logging.error(msg)
2 changes: 1 addition & 1 deletion tests/synapse_test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def client(self):
syn_user = os.getenv('SYNAPSE_USERNAME')
syn_pass = os.getenv('SYNAPSE_PASSWORD')

self._synapse_client = synapseclient.Synapse()
self._synapse_client = synapseclient.Synapse(skip_checks=True)
self._synapse_client.login(syn_user, syn_pass, silent=True)

return self._synapse_client
Expand Down
30 changes: 16 additions & 14 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
import src.synapse_uploader.cli as cli
from src.synapse_uploader.synapse_uploader import SynapseUploader

Expand All @@ -6,19 +7,20 @@ def test_cli(mocker):
args = ['', 'syn123', '/tmp', '-r', '10', '-d', '20', '-t', '30', '-u', '40', '-p', '50', '-ll', 'debug', '-f',
'-cd', '/tmp/cache']
mocker.patch('sys.argv', args)
mocker.patch('src.synapse_uploader.synapse_uploader.SynapseUploader.execute', return_value=None)
mock_init = mocker.patch.object(SynapseUploader, '__init__', return_value=None)
mocker.patch('src.synapse_uploader.synapse_uploader.SynapseUploader.execute')
mock_init = mocker.spy(SynapseUploader, '__init__')

cli.main()
with pytest.raises(SystemExit):
cli.main()

mock_init.assert_called_once_with(
'syn123',
'/tmp',
remote_path='10',
max_depth=20,
max_threads=30,
username='40',
password='50',
force_upload=True,
cache_dir='/tmp/cache'
)
mock_init.assert_called_once_with(mocker.ANY,
'syn123',
'/tmp',
remote_path='10',
max_depth=20,
max_threads=30,
username='40',
password='50',
force_upload=True,
cache_dir='/tmp/cache'
)
33 changes: 13 additions & 20 deletions tests/test_synapse_uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,13 @@ def test_max_depth_value():
syn_uploader = SynapseUploader('None', 'None', max_depth=max_depth)
assert syn_uploader._max_depth == max_depth

with pytest.raises(Exception) as ex:
SynapseUploader('None', 'None', max_depth=(SynapseUploader.MAX_SYNAPSE_DEPTH + 1))
assert str(ex.value) == 'Maximum depth must be less than or equal to 10000.'
errors = SynapseUploader('None', 'None', max_depth=(SynapseUploader.MAX_SYNAPSE_DEPTH + 1)).execute().errors
assert 'Maximum depth must be less than or equal to 10000.' in errors


def test_min_depth_value():
with pytest.raises(Exception) as ex:
SynapseUploader('None', 'None', max_depth=(SynapseUploader.MIN_SYNAPSE_DEPTH - 1))
assert str(ex.value) == 'Maximum depth must be greater than or equal to 2.'
errors = SynapseUploader('None', 'None', max_depth=(SynapseUploader.MIN_SYNAPSE_DEPTH - 1)).execute().errors
assert 'Maximum depth must be greater than or equal to 2.' in errors


def test_username_value():
Expand Down Expand Up @@ -299,24 +297,19 @@ def test_upload_file(syn_client, syn_test_helper, new_syn_project, new_temp_file
assert len(syn_folders) == 0
assert file_name in syn_file_names

# Test exceptions
with pytest.raises(Exception) as ex:
SynapseUploader(syn_file.id, new_temp_dir, synapse_client=syn_client).execute()
assert 'Local entity must be a file when remote entity is a file:' in str(ex.value)
# Test validations
errors = SynapseUploader(syn_file.id, new_temp_dir, synapse_client=syn_client).execute().errors
assert 'Local entity must be a file when remote entity is a file: {0}'.format(new_temp_dir) in errors

with pytest.raises(Exception) as ex:
SynapseUploader(syn_file.id, new_temp_file, remote_path='/test', synapse_client=syn_client).execute()
assert 'Cannot specify a remote path when remote entity is a file:' in str(ex.value)
errors = SynapseUploader(syn_file.id, new_temp_file, remote_path='/test',
synapse_client=syn_client).execute().errors
assert 'Cannot specify a remote path when remote entity is a file: {0}'.format(new_temp_file) in errors

# Local filename: {0} does not match remote file name:
other_temp_file = mkfile(new_temp_dir, syn_test_helper.uniq_name())
other_temp_file_name = os.path.basename(other_temp_file)
other_syn_file = syn_test_helper.create_file(name=other_temp_file_name,
path=other_temp_file,
parent=new_syn_project)
with pytest.raises(Exception) as ex:
SynapseUploader(syn_file.id, other_temp_file, synapse_client=syn_client).execute()
assert 'Local filename: {0} does not match remote file name:'.format(other_temp_file_name) in str(ex.value)
errors = SynapseUploader(syn_file.id, other_temp_file, synapse_client=syn_client).execute().errors
assert 'Local filename: {0} does not match remote file name: {1}'.format(other_temp_file_name,
syn_file.name) in errors


def test_force_upload(syn_client, syn_test_helper, new_syn_project, new_temp_file):
Expand Down

0 comments on commit f91dfb9

Please sign in to comment.