seshat 26.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- seshat/__init__.py +0 -0
- seshat/config.py +9 -0
- seshat/ui.py +133 -0
- seshat/utils/constants.py +63 -0
- seshat/utils/convenience.py +364 -0
- seshat/utils/rdf.py +273 -0
- seshat/web/cache.py +123 -0
- seshat/web/config.py +118 -0
- seshat/web/database.py +3835 -0
- seshat/web/email_handler.py +84 -0
- seshat/web/formatter.py +783 -0
- seshat/web/locks.py +70 -0
- seshat/web/resources/html_templates/400.html +5 -0
- seshat/web/resources/html_templates/403.html +5 -0
- seshat/web/resources/html_templates/404.html +5 -0
- seshat/web/resources/html_templates/410.html +5 -0
- seshat/web/resources/html_templates/500.html +6 -0
- seshat/web/resources/html_templates/502.html +6 -0
- seshat/web/resources/html_templates/503.html +7 -0
- seshat/web/resources/html_templates/activate_2fa_session.html +15 -0
- seshat/web/resources/html_templates/admin/dashboard.html +29 -0
- seshat/web/resources/html_templates/admin/exploratory.html +67 -0
- seshat/web/resources/html_templates/admin/quota_requests.html +56 -0
- seshat/web/resources/html_templates/admin/reports/dashboard.html +55 -0
- seshat/web/resources/html_templates/admin/reports/embargoed_datasets.html +69 -0
- seshat/web/resources/html_templates/admin/reports/restricted_datasets.html +64 -0
- seshat/web/resources/html_templates/admin/sparql.html +37 -0
- seshat/web/resources/html_templates/admin/users.html +72 -0
- seshat/web/resources/html_templates/author.html +116 -0
- seshat/web/resources/html_templates/badge.svg +46 -0
- seshat/web/resources/html_templates/categories.html +76 -0
- seshat/web/resources/html_templates/category.html +21 -0
- seshat/web/resources/html_templates/collection.html +26 -0
- seshat/web/resources/html_templates/colors.css +57 -0
- seshat/web/resources/html_templates/dataset.html +127 -0
- seshat/web/resources/html_templates/dataset_access_request.html +21 -0
- seshat/web/resources/html_templates/depositor/dashboard.html +116 -0
- seshat/web/resources/html_templates/depositor/edit-collection.html +230 -0
- seshat/web/resources/html_templates/depositor/edit-dataset.html +560 -0
- seshat/web/resources/html_templates/depositor/edit-session.html +81 -0
- seshat/web/resources/html_templates/depositor/language-selector.html +92 -0
- seshat/web/resources/html_templates/depositor/my-collections.html +89 -0
- seshat/web/resources/html_templates/depositor/my-data.html +113 -0
- seshat/web/resources/html_templates/depositor/new_private_link.html +52 -0
- seshat/web/resources/html_templates/depositor/private-links.html +48 -0
- seshat/web/resources/html_templates/depositor/profile.html +135 -0
- seshat/web/resources/html_templates/depositor/published-collection.html +15 -0
- seshat/web/resources/html_templates/depositor/submitted-for-review.html +15 -0
- seshat/web/resources/html_templates/email/2fa_token.html +8 -0
- seshat/web/resources/html_templates/email/2fa_token.txt +7 -0
- seshat/web/resources/html_templates/email/data_access_request.html +13 -0
- seshat/web/resources/html_templates/email/data_access_request.txt +14 -0
- seshat/web/resources/html_templates/email/dataset_approved.html +18 -0
- seshat/web/resources/html_templates/email/dataset_approved.txt +22 -0
- seshat/web/resources/html_templates/email/dataset_declined.html +8 -0
- seshat/web/resources/html_templates/email/dataset_declined.txt +8 -0
- seshat/web/resources/html_templates/email/dataset_submitted.html +8 -0
- seshat/web/resources/html_templates/email/dataset_submitted.txt +7 -0
- seshat/web/resources/html_templates/email/declined_dataset_notification.html +10 -0
- seshat/web/resources/html_templates/email/declined_dataset_notification.txt +6 -0
- seshat/web/resources/html_templates/email/feedback.html +10 -0
- seshat/web/resources/html_templates/email/feedback.txt +10 -0
- seshat/web/resources/html_templates/email/layout.html +12 -0
- seshat/web/resources/html_templates/email/published_dataset_notification.html +10 -0
- seshat/web/resources/html_templates/email/published_dataset_notification.txt +6 -0
- seshat/web/resources/html_templates/email/quota_approved.html +8 -0
- seshat/web/resources/html_templates/email/quota_approved.txt +9 -0
- seshat/web/resources/html_templates/email/quota_request.html +10 -0
- seshat/web/resources/html_templates/email/quota_request.txt +9 -0
- seshat/web/resources/html_templates/email/submitted_for_review_notification.html +8 -0
- seshat/web/resources/html_templates/email/submitted_for_review_notification.txt +10 -0
- seshat/web/resources/html_templates/feedback.html +53 -0
- seshat/web/resources/html_templates/git_instructions.html +7 -0
- seshat/web/resources/html_templates/institutions.html +58 -0
- seshat/web/resources/html_templates/jsonld.html +73 -0
- seshat/web/resources/html_templates/layout.html +123 -0
- seshat/web/resources/html_templates/loader.svg +51 -0
- seshat/web/resources/html_templates/maintenance.html +5 -0
- seshat/web/resources/html_templates/portal.html +62 -0
- seshat/web/resources/html_templates/private_link_is_expired.html +5 -0
- seshat/web/resources/html_templates/public_metadata.html +310 -0
- seshat/web/resources/html_templates/review/overview.html +102 -0
- seshat/web/resources/html_templates/review/published.html +14 -0
- seshat/web/resources/html_templates/search.html +322 -0
- seshat/web/resources/html_templates/sitemap_template.xml +11 -0
- seshat/web/resources/languages.json +91 -0
- seshat/web/resources/licenses.json +156 -0
- seshat/web/resources/metadata_templates/bibtex.bib +10 -0
- seshat/web/resources/metadata_templates/citation.cff +21 -0
- seshat/web/resources/metadata_templates/endnote.enw +18 -0
- seshat/web/resources/metadata_templates/refman.ris +19 -0
- seshat/web/resources/root_categories.json +296 -0
- seshat/web/resources/sparql_templates/account_by_email.sparql +35 -0
- seshat/web/resources/sparql_templates/account_by_session_token.sparql +44 -0
- seshat/web/resources/sparql_templates/account_categories.sparql +28 -0
- seshat/web/resources/sparql_templates/account_sessions.sparql +26 -0
- seshat/web/resources/sparql_templates/account_storage_used.sparql +23 -0
- seshat/web/resources/sparql_templates/account_uuid_by_orcid.sparql +14 -0
- seshat/web/resources/sparql_templates/accounts.sparql +67 -0
- seshat/web/resources/sparql_templates/append_to_list.sparql +19 -0
- seshat/web/resources/sparql_templates/associated_authors.sparql +16 -0
- seshat/web/resources/sparql_templates/author_profile.sparql +34 -0
- seshat/web/resources/sparql_templates/author_public_items.sparql +34 -0
- seshat/web/resources/sparql_templates/authors.sparql +84 -0
- seshat/web/resources/sparql_templates/categories.sparql +62 -0
- seshat/web/resources/sparql_templates/categories_tree.sparql +17 -0
- seshat/web/resources/sparql_templates/category_by_id.sparql +21 -0
- seshat/web/resources/sparql_templates/collaborators.sparql +75 -0
- seshat/web/resources/sparql_templates/collection_dataset_containers.sparql +8 -0
- seshat/web/resources/sparql_templates/collection_datasets.sparql +19 -0
- seshat/web/resources/sparql_templates/collection_datasets_count.sparql +11 -0
- seshat/web/resources/sparql_templates/collection_versions.sparql +20 -0
- seshat/web/resources/sparql_templates/collections.sparql +107 -0
- seshat/web/resources/sparql_templates/collections_by_account.sparql +18 -0
- seshat/web/resources/sparql_templates/collections_from_dataset.sparql +18 -0
- seshat/web/resources/sparql_templates/contact_info_from_container.sparql +14 -0
- seshat/web/resources/sparql_templates/container.sparql +28 -0
- seshat/web/resources/sparql_templates/container_items.sparql +58 -0
- seshat/web/resources/sparql_templates/container_uuid_by_id.sparql +13 -0
- seshat/web/resources/sparql_templates/custom_fields.sparql +33 -0
- seshat/web/resources/sparql_templates/dataset_files.sparql +90 -0
- seshat/web/resources/sparql_templates/dataset_is_under_review.sparql +11 -0
- seshat/web/resources/sparql_templates/dataset_statistics.sparql +26 -0
- seshat/web/resources/sparql_templates/dataset_statistics_timeline.sparql +21 -0
- seshat/web/resources/sparql_templates/dataset_storage_used.sparql +12 -0
- seshat/web/resources/sparql_templates/dataset_versions.sparql +23 -0
- seshat/web/resources/sparql_templates/datasets.sparql +238 -0
- seshat/web/resources/sparql_templates/decline_draft_dataset.sparql +28 -0
- seshat/web/resources/sparql_templates/delete_account_groups_associations.sparql +15 -0
- seshat/web/resources/sparql_templates/delete_account_property.sparql +15 -0
- seshat/web/resources/sparql_templates/delete_associations.sparql +40 -0
- seshat/web/resources/sparql_templates/delete_collaborator.sparql +29 -0
- seshat/web/resources/sparql_templates/delete_collection_draft.sparql +29 -0
- seshat/web/resources/sparql_templates/delete_dataset_draft.sparql +39 -0
- seshat/web/resources/sparql_templates/delete_dataset_embargo.sparql +23 -0
- seshat/web/resources/sparql_templates/delete_group_members.sparql +21 -0
- seshat/web/resources/sparql_templates/delete_inactive_session_by_uuid.sparql +17 -0
- seshat/web/resources/sparql_templates/delete_inferred_groups.sparql +21 -0
- seshat/web/resources/sparql_templates/delete_item_from_list.sparql +30 -0
- seshat/web/resources/sparql_templates/delete_items_all_from_list.sparql +24 -0
- seshat/web/resources/sparql_templates/delete_private_links.sparql +25 -0
- seshat/web/resources/sparql_templates/delete_session.sparql +17 -0
- seshat/web/resources/sparql_templates/delete_session_by_uuid.sparql +18 -0
- seshat/web/resources/sparql_templates/delete_sessions.sparql +14 -0
- seshat/web/resources/sparql_templates/delete_sessions_older_than.sparql +17 -0
- seshat/web/resources/sparql_templates/derived_from.sparql +10 -0
- seshat/web/resources/sparql_templates/explorer_properties.sparql +11 -0
- seshat/web/resources/sparql_templates/explorer_property_types.sparql +12 -0
- seshat/web/resources/sparql_templates/explorer_types.sparql +10 -0
- seshat/web/resources/sparql_templates/export_listable_type.sparql +22 -0
- seshat/web/resources/sparql_templates/export_root_type.sparql +19 -0
- seshat/web/resources/sparql_templates/funding.sparql +33 -0
- seshat/web/resources/sparql_templates/group.sparql +23 -0
- seshat/web/resources/sparql_templates/group_by_name.sparql +16 -0
- seshat/web/resources/sparql_templates/item_collaborative_permissions.sparql +40 -0
- seshat/web/resources/sparql_templates/latest_datasets_portal.sparql +17 -0
- seshat/web/resources/sparql_templates/licenses.sparql +16 -0
- seshat/web/resources/sparql_templates/members.sparql +32 -0
- seshat/web/resources/sparql_templates/missing_checksummed_files_for_container.sparql +16 -0
- seshat/web/resources/sparql_templates/missing_dois.sparql +16 -0
- seshat/web/resources/sparql_templates/prefixes.sparql +5 -0
- seshat/web/resources/sparql_templates/private_links.sparql +34 -0
- seshat/web/resources/sparql_templates/projects.sparql +28 -0
- seshat/web/resources/sparql_templates/publish_draft_collection.sparql +59 -0
- seshat/web/resources/sparql_templates/publish_draft_dataset.sparql +66 -0
- seshat/web/resources/sparql_templates/quota_requests.sparql +28 -0
- seshat/web/resources/sparql_templates/record_uri.sparql +10 -0
- seshat/web/resources/sparql_templates/references.sparql +43 -0
- seshat/web/resources/sparql_templates/reviews.sparql +78 -0
- seshat/web/resources/sparql_templates/root_categories.sparql +17 -0
- seshat/web/resources/sparql_templates/search_tags.sparql +14 -0
- seshat/web/resources/sparql_templates/statistics_authors.sparql +16 -0
- seshat/web/resources/sparql_templates/statistics_collections.sparql +13 -0
- seshat/web/resources/sparql_templates/statistics_datalink.sparql +13 -0
- seshat/web/resources/sparql_templates/statistics_datasets.sparql +13 -0
- seshat/web/resources/sparql_templates/statistics_files.sparql +32 -0
- seshat/web/resources/sparql_templates/subcategories_by_category.sparql +19 -0
- seshat/web/resources/sparql_templates/tags.sparql +47 -0
- seshat/web/resources/sparql_templates/transfer_account_resources.sparql +19 -0
- seshat/web/resources/sparql_templates/update_account.sparql +117 -0
- seshat/web/resources/sparql_templates/update_author.sparql +49 -0
- seshat/web/resources/sparql_templates/update_authors_order.sparql +34 -0
- seshat/web/resources/sparql_templates/update_collaborators.sparql +52 -0
- seshat/web/resources/sparql_templates/update_collection.sparql +110 -0
- seshat/web/resources/sparql_templates/update_dataset.sparql +229 -0
- seshat/web/resources/sparql_templates/update_dataset_thumb.sparql +36 -0
- seshat/web/resources/sparql_templates/update_doi_after_publishing.sparql +20 -0
- seshat/web/resources/sparql_templates/update_file.sparql +105 -0
- seshat/web/resources/sparql_templates/update_git_uuid.sparql +21 -0
- seshat/web/resources/sparql_templates/update_orcid_for_account.sparql +28 -0
- seshat/web/resources/sparql_templates/update_private_link.sparql +47 -0
- seshat/web/resources/sparql_templates/update_quota_request.sparql +46 -0
- seshat/web/resources/sparql_templates/update_review.sparql +36 -0
- seshat/web/resources/sparql_templates/update_seen_by_reviewer.sparql +20 -0
- seshat/web/resources/sparql_templates/update_session.sparql +35 -0
- seshat/web/resources/sparql_templates/update_view_and_download_counts.sparql +50 -0
- seshat/web/resources/static/css/ace.css +1 -0
- seshat/web/resources/static/css/dropzone.min.css +1 -0
- seshat/web/resources/static/css/form.css +191 -0
- seshat/web/resources/static/css/jquery.dataTables.min.css +1 -0
- seshat/web/resources/static/css/main.css +626 -0
- seshat/web/resources/static/css/pub.css +63 -0
- seshat/web/resources/static/css/quill.snow.css +10 -0
- seshat/web/resources/static/fonts/FiraCode-Regular.woff2 +0 -0
- seshat/web/resources/static/fonts/SourceSansPro-Bold.ttf +0 -0
- seshat/web/resources/static/fonts/SourceSansPro-Italic.ttf +0 -0
- seshat/web/resources/static/fonts/SourceSansPro-Regular.ttf +0 -0
- seshat/web/resources/static/fonts/fa-brands-400.eot +0 -0
- seshat/web/resources/static/fonts/fa-brands-400.ttf +0 -0
- seshat/web/resources/static/fonts/fa-brands-400.woff +0 -0
- seshat/web/resources/static/fonts/fa-brands-400.woff2 +0 -0
- seshat/web/resources/static/fonts/fa-solid-900.eot +0 -0
- seshat/web/resources/static/fonts/fa-solid-900.ttf +0 -0
- seshat/web/resources/static/fonts/fa-solid-900.woff +0 -0
- seshat/web/resources/static/fonts/fa-solid-900.woff2 +0 -0
- seshat/web/resources/static/images/codeworks-badge.svg +2 -0
- seshat/web/resources/static/images/collection-thumb.svg +33 -0
- seshat/web/resources/static/images/dataset-thumb.svg +86 -0
- seshat/web/resources/static/images/datatables/sort_asc.png +0 -0
- seshat/web/resources/static/images/datatables/sort_asc_disabled.png +0 -0
- seshat/web/resources/static/images/datatables/sort_both.png +0 -0
- seshat/web/resources/static/images/datatables/sort_desc.png +0 -0
- seshat/web/resources/static/images/datatables/sort_desc_disabled.png +0 -0
- seshat/web/resources/static/images/ext-link.png +0 -0
- seshat/web/resources/static/images/handle-logo.png +0 -0
- seshat/web/resources/static/images/logo.png +0 -0
- seshat/web/resources/static/images/logo.svg +2 -0
- seshat/web/resources/static/images/orcid.svg +17 -0
- seshat/web/resources/static/js/ace/ace.js +17 -0
- seshat/web/resources/static/js/ace/ext-language_tools.js +8 -0
- seshat/web/resources/static/js/ace/ext-searchbox.js +8 -0
- seshat/web/resources/static/js/ace/mode-sparql.js +8 -0
- seshat/web/resources/static/js/ace/theme-crimson_editor.js +8 -0
- seshat/web/resources/static/js/admin-dashboard.js +40 -0
- seshat/web/resources/static/js/d3.v7.min.js +2 -0
- seshat/web/resources/static/js/depositor-dashboard.js +61 -0
- seshat/web/resources/static/js/dropzone.min.js +1 -0
- seshat/web/resources/static/js/edit-collection.js +640 -0
- seshat/web/resources/static/js/edit-dataset.js +1673 -0
- seshat/web/resources/static/js/edit-profile.js +100 -0
- seshat/web/resources/static/js/exploratory.js +185 -0
- seshat/web/resources/static/js/jquery-3.6.0.min.js +2 -0
- seshat/web/resources/static/js/jquery.dataTables.min.js +187 -0
- seshat/web/resources/static/js/landing_page.js +133 -0
- seshat/web/resources/static/js/my-data.js +27 -0
- seshat/web/resources/static/js/query-editor.js +98 -0
- seshat/web/resources/static/js/quill.min.js +3 -0
- seshat/web/resources/static/js/ranking.js +84 -0
- seshat/web/resources/static/js/review-overview.js +211 -0
- seshat/web/resources/static/js/search.js +924 -0
- seshat/web/resources/static/js/utils.js +568 -0
- seshat/web/s3.py +151 -0
- seshat/web/ui.py +1436 -0
- seshat/web/validator.py +525 -0
- seshat/web/wsgi.py +10267 -0
- seshat/web/xml_formatter.py +360 -0
- seshat/web/zipfly.py +184 -0
- seshat-26.7.dist-info/METADATA +185 -0
- seshat-26.7.dist-info/RECORD +263 -0
- seshat-26.7.dist-info/WHEEL +5 -0
- seshat-26.7.dist-info/entry_points.txt +2 -0
- seshat-26.7.dist-info/licenses/LICENSE +16 -0
- seshat-26.7.dist-info/top_level.txt +1 -0
seshat/__init__.py
ADDED
|
File without changes
|
seshat/config.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module helps the code find stuff outside of the Python package's scope.
|
|
3
|
+
It is optional, and only available when built using the autotools-based build.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
PREFIX = "/usr/local"
|
|
8
|
+
DATA_ROOT_DIR = "${prefix}/share".replace("${prefix}", PREFIX)
|
|
9
|
+
DOCUMENTATION_DIRECTORY = os.path.join (DATA_ROOT_DIR, "seshat")
|
seshat/ui.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the entry point for the program.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import signal
|
|
7
|
+
import sys
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import importlib.metadata
|
|
11
|
+
|
|
12
|
+
import seshat.web.ui as web_ui
|
|
13
|
+
|
|
14
|
+
def show_version ():
|
|
15
|
+
"""Show the program's version."""
|
|
16
|
+
|
|
17
|
+
version = importlib.metadata.version (__package__ or __name__)
|
|
18
|
+
print(f"This is seshat v{version}")
|
|
19
|
+
sys.exit(0)
|
|
20
|
+
|
|
21
|
+
def show_help ():
|
|
22
|
+
"""Show a GNU-style help message."""
|
|
23
|
+
|
|
24
|
+
print("""This is seshat.\n
|
|
25
|
+
Available subcommands and options:
|
|
26
|
+
|
|
27
|
+
web:
|
|
28
|
+
--help -h Show a help message.
|
|
29
|
+
--config-file=ARG -c Load configuration from a file.
|
|
30
|
+
--initialize -i Populate the RDF store with default triples.
|
|
31
|
+
--extract-transactions-from-log=ARG
|
|
32
|
+
-e Extract transactions from the log file. It does
|
|
33
|
+
not extract delayed transactions, so it matches
|
|
34
|
+
what would have been applied to the database
|
|
35
|
+
normally. The optional argument is a datetime to
|
|
36
|
+
start from, in the format of YYYY-MM-DD HH:MM:SS.
|
|
37
|
+
--extract-delayed-transactions-from-log=ARG
|
|
38
|
+
-d Similar to -e, except this option only extracts
|
|
39
|
+
delayed transactions from the log file.
|
|
40
|
+
--apply-transactions=ARG
|
|
41
|
+
-A Apply transactions extracted by the -e option above.
|
|
42
|
+
The optional argument is the path to the folder that
|
|
43
|
+
contains the SPARQL transaction files. By default
|
|
44
|
+
it will look in the pre-configured directory.
|
|
45
|
+
--full-rdf-export -f Exports all metadata as RDF triples in N3 format.
|
|
46
|
+
This feature is in BETA state. Do not rely on it
|
|
47
|
+
for a full back-up.
|
|
48
|
+
--public-rdf-export -p Exports all publically available metadata as RDF
|
|
49
|
+
triples in N3 format. This feature is in BETA
|
|
50
|
+
state. Do not reply on it for an accurate
|
|
51
|
+
representation of publically available metadata.
|
|
52
|
+
|
|
53
|
+
Global options:
|
|
54
|
+
--help -h Show this message.
|
|
55
|
+
--version -v Show versioning information.\n""")
|
|
56
|
+
sys.exit(0)
|
|
57
|
+
|
|
58
|
+
def sigint_handler (sig, frame): # pylint: disable=unused-argument
|
|
59
|
+
"""Signal handler for SIGINT and SIGTERM."""
|
|
60
|
+
logger = logging.getLogger(__name__)
|
|
61
|
+
logger.info ("Received shutdown signal. Goodbye!")
|
|
62
|
+
sys.exit(0)
|
|
63
|
+
|
|
64
|
+
def main_inner ():
|
|
65
|
+
"""The main entry point of the program."""
|
|
66
|
+
|
|
67
|
+
signal.signal(signal.SIGINT, sigint_handler)
|
|
68
|
+
signal.signal(signal.SIGTERM, sigint_handler)
|
|
69
|
+
|
|
70
|
+
logging.basicConfig(format='[%(levelname)s] %(asctime)s - %(name)s: %(message)s',
|
|
71
|
+
level=logging.INFO)
|
|
72
|
+
|
|
73
|
+
## COMMAND-LINE ARGUMENTS
|
|
74
|
+
## ------------------------------------------------------------------------
|
|
75
|
+
parser = argparse.ArgumentParser(
|
|
76
|
+
usage = '\n %(prog)s [web] ...',
|
|
77
|
+
prog = 'seshat',
|
|
78
|
+
add_help = False)
|
|
79
|
+
|
|
80
|
+
subparsers = parser.add_subparsers(dest='command', help='sub-command help')
|
|
81
|
+
|
|
82
|
+
### WEB SUBCOMMAND
|
|
83
|
+
### -----------------------------------------------------------------------
|
|
84
|
+
web_parser = subparsers.add_parser('web', help="Options for the 'web' subcommand.")
|
|
85
|
+
web_parser.add_argument('--config-file','-c', type=str, default=None)
|
|
86
|
+
web_parser.add_argument('--initialize', '-i', action='store_true')
|
|
87
|
+
web_parser.add_argument('--extract-transactions-from-log', '-e', nargs='?',
|
|
88
|
+
const='', default=None)
|
|
89
|
+
web_parser.add_argument('--extract-delayed-transactions-from-log', '-d', nargs='?',
|
|
90
|
+
const='', default=None)
|
|
91
|
+
web_parser.add_argument('--apply-transactions', '-A', nargs='?',
|
|
92
|
+
const='', default=None)
|
|
93
|
+
web_parser.add_argument('--full-rdf-export', '-f', action='store_true')
|
|
94
|
+
web_parser.add_argument('--public-rdf-export', '-p', action='store_true')
|
|
95
|
+
|
|
96
|
+
### GLOBAL ARGUMENTS
|
|
97
|
+
### -----------------------------------------------------------------------
|
|
98
|
+
parser.add_argument('--help', '-h', action='store_true')
|
|
99
|
+
parser.add_argument('--version', '-v', action='store_true')
|
|
100
|
+
|
|
101
|
+
# When using PyInstaller and Nuitka, argv[0] seems to get duplicated.
|
|
102
|
+
# In the case of Nuitka, relative paths are converted to absolute paths.
|
|
103
|
+
# This bit de-duplicates argv[0] in these cases.
|
|
104
|
+
try:
|
|
105
|
+
if os.path.abspath(sys.argv[0]) == os.path.abspath(sys.argv[1]):
|
|
106
|
+
sys.argv = sys.argv[1:]
|
|
107
|
+
except IndexError:
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
args = parser.parse_args()
|
|
111
|
+
if args.help:
|
|
112
|
+
show_help()
|
|
113
|
+
if args.version:
|
|
114
|
+
show_version()
|
|
115
|
+
|
|
116
|
+
if args.command == "web":
|
|
117
|
+
web_ui.main (args.config_file, True, args.initialize,
|
|
118
|
+
args.extract_transactions_from_log,
|
|
119
|
+
args.extract_delayed_transactions_from_log,
|
|
120
|
+
args.apply_transactions, args.full_rdf_export,
|
|
121
|
+
args.public_rdf_export)
|
|
122
|
+
|
|
123
|
+
elif len(sys.argv) == 1:
|
|
124
|
+
print("Try --help for usage options.")
|
|
125
|
+
|
|
126
|
+
def main ():
|
|
127
|
+
"""Wrapper to catch KeyboardInterrupts for main_inner."""
|
|
128
|
+
try:
|
|
129
|
+
main_inner()
|
|
130
|
+
except KeyboardInterrupt:
|
|
131
|
+
sigint_handler (None, None)
|
|
132
|
+
|
|
133
|
+
sys.exit(0)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides constants that can be used throughout
|
|
3
|
+
the codebase.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
filetypes_by_extension = {
|
|
7
|
+
"bash": "Shell",
|
|
8
|
+
"bib": "TeX",
|
|
9
|
+
"c": "C",
|
|
10
|
+
"cc": "C++",
|
|
11
|
+
"cpp": "C++",
|
|
12
|
+
"cs": "C#",
|
|
13
|
+
"css": "CSS",
|
|
14
|
+
"cxx": "C++",
|
|
15
|
+
"d": "D",
|
|
16
|
+
"f": "Fortran",
|
|
17
|
+
"f77": "Fortran",
|
|
18
|
+
"f90": "Fortran",
|
|
19
|
+
"for": "Fortran",
|
|
20
|
+
"geojson": "JSON",
|
|
21
|
+
"go": "Go",
|
|
22
|
+
"gohtml": "Go",
|
|
23
|
+
"h": "C",
|
|
24
|
+
"hpp": "C++",
|
|
25
|
+
"hs": "Haskell",
|
|
26
|
+
"html": "HTML",
|
|
27
|
+
"htm": "HTML",
|
|
28
|
+
"hxx": "C++",
|
|
29
|
+
"in": "Automake",
|
|
30
|
+
"ipynb": "Jupyter Notebook",
|
|
31
|
+
"java": "Java",
|
|
32
|
+
"jl": "Julia",
|
|
33
|
+
"js": "JavaScript",
|
|
34
|
+
"json": "JSON",
|
|
35
|
+
"m": "Matlab",
|
|
36
|
+
"mat": "Matlab",
|
|
37
|
+
"md": "Markdown",
|
|
38
|
+
"mts": "TypeScript",
|
|
39
|
+
"mysql": "SQL",
|
|
40
|
+
"php": "PHP",
|
|
41
|
+
"pl": "Perl",
|
|
42
|
+
"pm": "Perl",
|
|
43
|
+
"py": "Python",
|
|
44
|
+
"r": "R",
|
|
45
|
+
"rb": "Ruby",
|
|
46
|
+
"rs": "Rust",
|
|
47
|
+
"sh": "Shell",
|
|
48
|
+
"sparql": "SPARQL",
|
|
49
|
+
"sql": "SQL",
|
|
50
|
+
"sty": "TeX",
|
|
51
|
+
"tex": "TeX",
|
|
52
|
+
"ts": "TypeScript",
|
|
53
|
+
"tsx": "TypeScript",
|
|
54
|
+
"xml": "XML",
|
|
55
|
+
"xyz": "Chemical XYZ",
|
|
56
|
+
"yaml": "YAML",
|
|
57
|
+
"yml": "YAML",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
allowed_html_tags = ["p", "strong", "em", "u", "s", "ol", "ul", "li", "code", "pre", "br", "sup", "sub", "h2", "h3"]
|
|
61
|
+
|
|
62
|
+
iiif_supported_formats = [ ".jpg", ".png", ".pdf", ".tif", ".tiff", ".gif", ".webp" ]
|
|
63
|
+
datetime_format = "%Y-%m-%dT%H:%M:%S" # pylint: disable=invalid-name
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides convenience functions that can be used throughout
|
|
3
|
+
the codebase.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
import logging
|
|
8
|
+
from html.parser import HTMLParser
|
|
9
|
+
from html import escape, unescape
|
|
10
|
+
from seshat.utils.constants import allowed_html_tags
|
|
11
|
+
|
|
12
|
+
RLIMIT_AVAILABLE = False # pylint: disable=invalid-name
|
|
13
|
+
try:
|
|
14
|
+
from resource import setrlimit, RLIMIT_AS, RLIM_INFINITY
|
|
15
|
+
RLIMIT_AVAILABLE = True # pylint: disable=invalid-name
|
|
16
|
+
except ImportError:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
class HTMLStripper (HTMLParser):
|
|
20
|
+
"""Overriden HTMLParser to strip HTML tags inspired by Django's implementation."""
|
|
21
|
+
|
|
22
|
+
def __init__ (self):
|
|
23
|
+
super().__init__(convert_charrefs=False)
|
|
24
|
+
self.reset()
|
|
25
|
+
self.state = []
|
|
26
|
+
|
|
27
|
+
def handle_data (self, data):
|
|
28
|
+
self.state.append (data)
|
|
29
|
+
|
|
30
|
+
def handle_entityref (self, name):
|
|
31
|
+
self.state.append (f"&{name};")
|
|
32
|
+
|
|
33
|
+
def handle_charref (self, name):
|
|
34
|
+
self.state.append (f"&#{name};")
|
|
35
|
+
|
|
36
|
+
def get_data (self):
|
|
37
|
+
"""Return stripped HTML."""
|
|
38
|
+
return "".join(self.state)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def html_to_plaintext (value, respect_newlines=False):
|
|
42
|
+
"""
|
|
43
|
+
Outputs plain text without HTML tags.
|
|
44
|
+
When RESPECT_NEWLINES is set to True, it will convert line
|
|
45
|
+
breaks to newline characters.
|
|
46
|
+
"""
|
|
47
|
+
if respect_newlines:
|
|
48
|
+
value = value.replace("</p>", "</p>\n\n")
|
|
49
|
+
value = value.replace("<br>", "\n")
|
|
50
|
+
value = value.replace("<br/>", "\n")
|
|
51
|
+
value = value.replace("<br />", "\n")
|
|
52
|
+
|
|
53
|
+
while "<" in value and ">" in value:
|
|
54
|
+
tag_count = value.count("<")
|
|
55
|
+
html = HTMLStripper()
|
|
56
|
+
html.feed (value)
|
|
57
|
+
html.close()
|
|
58
|
+
value = html.get_data()
|
|
59
|
+
if tag_count == value.count("<"):
|
|
60
|
+
break
|
|
61
|
+
|
|
62
|
+
return value
|
|
63
|
+
|
|
64
|
+
def contains_disallowed_html (value):
|
|
65
|
+
"""Return True when there is a disallowed tag in VALUE, False otherwise."""
|
|
66
|
+
|
|
67
|
+
if value is None:
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
stripped_value = unescape (value)
|
|
71
|
+
for tag in allowed_html_tags:
|
|
72
|
+
stripped_value = stripped_value.replace (f"<{tag}>", "")
|
|
73
|
+
stripped_value = stripped_value.replace (f"</{tag}>", "")
|
|
74
|
+
|
|
75
|
+
return html_to_plaintext (stripped_value) != stripped_value
|
|
76
|
+
|
|
77
|
+
def encode_html (value, allow_simple_tags=True, allow_some_punctuation=True):
|
|
78
|
+
"""
|
|
79
|
+
Returns VALUE but with encoded HTML entities. When ALLOW_SIMPLE_TAGS is
|
|
80
|
+
True, it doesn't encode HTML tags in constants.allowed_html_tags.
|
|
81
|
+
"""
|
|
82
|
+
encoded_value = escape (value)
|
|
83
|
+
if allow_simple_tags:
|
|
84
|
+
for tag in allowed_html_tags:
|
|
85
|
+
encoded_value = encoded_value.replace (f"<{tag}>", f"<{tag}>")
|
|
86
|
+
encoded_value = encoded_value.replace (f"</{tag}>", f"</{tag}>")
|
|
87
|
+
|
|
88
|
+
if allow_some_punctuation:
|
|
89
|
+
for character in [("&", "&"), ("\"", """), (" ", " ")]:
|
|
90
|
+
encoded_value = encoded_value.replace (character[1], character[0])
|
|
91
|
+
|
|
92
|
+
return encoded_value
|
|
93
|
+
|
|
94
|
+
def decode_html (value):
|
|
95
|
+
"""Reverses the effect of 'encode_html'."""
|
|
96
|
+
return unescape(value)
|
|
97
|
+
|
|
98
|
+
def value_or (record, key, other):
|
|
99
|
+
"""Return the value of KEY or OTHER."""
|
|
100
|
+
try:
|
|
101
|
+
return record[key]
|
|
102
|
+
except (IndexError, KeyError, TypeError):
|
|
103
|
+
return other
|
|
104
|
+
|
|
105
|
+
def value_or_none (record, key):
|
|
106
|
+
"""Return the value of KEY or None."""
|
|
107
|
+
return value_or (record, key, None)
|
|
108
|
+
|
|
109
|
+
def pretty_print_size (num_bytes):
|
|
110
|
+
"""Return pretty-printed file size."""
|
|
111
|
+
|
|
112
|
+
output = ""
|
|
113
|
+
if not isinstance(num_bytes, int):
|
|
114
|
+
output = "0B"
|
|
115
|
+
elif num_bytes < 1000:
|
|
116
|
+
output = f"{num_bytes}B"
|
|
117
|
+
elif num_bytes < 1000000:
|
|
118
|
+
output = f"{num_bytes/1000:.2f}KB"
|
|
119
|
+
elif num_bytes < 1000000000:
|
|
120
|
+
output = f"{num_bytes/1000000:.2f}MB"
|
|
121
|
+
elif num_bytes < 1000000000000:
|
|
122
|
+
output = f"{num_bytes/1000000000:.2f}GB"
|
|
123
|
+
elif num_bytes < 1000000000000000:
|
|
124
|
+
output = f"{num_bytes/1000000000000:.2f}TB"
|
|
125
|
+
else:
|
|
126
|
+
output = f"{num_bytes/1000000000000000:.2f}PB"
|
|
127
|
+
|
|
128
|
+
return output
|
|
129
|
+
|
|
130
|
+
def decimal_coord(raw_input, axis, digits=5):
|
|
131
|
+
'''
|
|
132
|
+
Converts txt to string with decimal coordinates or None if invalid.
|
|
133
|
+
Accepts input like "42.597" or "5º 38’ 18.5’’ E".
|
|
134
|
+
axis is 'N' or 'E'
|
|
135
|
+
5 digits is accuracy (RMS) of 40 cm.
|
|
136
|
+
'''
|
|
137
|
+
pattern = r"""^(-?\d+)[º°]\s*(\d+)[’']\s*((\d+)(\.\d?)?)((’’)|('')|"|”)\s*([NESW]?)$"""
|
|
138
|
+
if raw_input is None:
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
deg = float(raw_input)
|
|
143
|
+
except ValueError:
|
|
144
|
+
raw_input = str(raw_input).strip()
|
|
145
|
+
pattern_match = re.search(pattern, raw_input)
|
|
146
|
+
if not pattern_match:
|
|
147
|
+
return None # Undecipherable gibberish
|
|
148
|
+
|
|
149
|
+
components = pattern_match.groups()
|
|
150
|
+
deg = int(components[0]) + int(components[1])/60 + float(components[2])/3600
|
|
151
|
+
direction = components[-1] #direction may be N,E,S,W, axis N,E.
|
|
152
|
+
if (direction == 'S' and axis == 'N') or (direction == 'W' and axis == 'E'):
|
|
153
|
+
deg = -deg
|
|
154
|
+
elif direction not in (axis, ''):
|
|
155
|
+
return None # Probably mixed up lat and lon
|
|
156
|
+
|
|
157
|
+
max_deg = 90 if axis == 'N' else 180
|
|
158
|
+
if abs(deg) <= max_deg:
|
|
159
|
+
return f'{deg:.{digits}f}'
|
|
160
|
+
|
|
161
|
+
return None
|
|
162
|
+
|
|
163
|
+
def decimal_coords(lat, lon, digits=5):
|
|
164
|
+
'''
|
|
165
|
+
Converts raw input lat, lon to decimal coordinats or None if invalid.
|
|
166
|
+
'''
|
|
167
|
+
lat_validated = decimal_coord(lat, 'N', digits=digits)
|
|
168
|
+
lon_validated = decimal_coord(lon, 'E', digits=digits)
|
|
169
|
+
return (lat_validated, lon_validated)
|
|
170
|
+
|
|
171
|
+
def self_or_value_or_none(record, key):
|
|
172
|
+
'''
|
|
173
|
+
Return record[key]['value'] or record[key] or none.
|
|
174
|
+
Use: deal with triples where table2rdf transformed a string into a number.
|
|
175
|
+
'''
|
|
176
|
+
if key in record:
|
|
177
|
+
return value_or (record[key], 'value', record[key])
|
|
178
|
+
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
def parses_to_int (input_string):
|
|
182
|
+
"""Return True when wrapping in int() would succeed, False otherwise."""
|
|
183
|
+
try:
|
|
184
|
+
int(input_string)
|
|
185
|
+
except (ValueError, TypeError):
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
return True
|
|
189
|
+
|
|
190
|
+
def deduplicate_list (alist):
|
|
191
|
+
"""
|
|
192
|
+
Return deduplicated list, retaining original ordering,
|
|
193
|
+
based on first occurrence of duplicates.
|
|
194
|
+
"""
|
|
195
|
+
try:
|
|
196
|
+
return list(dict.fromkeys(alist))
|
|
197
|
+
except TypeError:
|
|
198
|
+
logging.error('Wrong type of %s in deduplicate_list', alist)
|
|
199
|
+
return None
|
|
200
|
+
|
|
201
|
+
def make_citation (authors, year, title, version, item_type, doi,
|
|
202
|
+
publisher=None, max_cited_authors=5):
|
|
203
|
+
"""Return citation in standard Datacite format."""
|
|
204
|
+
try:
|
|
205
|
+
auths = [{key:val for key,val in author.items() if val}
|
|
206
|
+
for author in authors] #remove empty name parts
|
|
207
|
+
citation = '; '.join([
|
|
208
|
+
(f"{author['last_name']}, {author['first_name']}" if
|
|
209
|
+
not {'first_name','last_name'}-set(author)
|
|
210
|
+
else value_or (author, 'full_name', "Unknown")) for author in auths[:max_cited_authors]])
|
|
211
|
+
if authors[max_cited_authors:max_cited_authors+1]:
|
|
212
|
+
citation += ' et. al.'
|
|
213
|
+
citation += f' ({year}): {title}'
|
|
214
|
+
if not citation.endswith('.'):
|
|
215
|
+
citation += '.'
|
|
216
|
+
|
|
217
|
+
publisher = f"{publisher}. " if publisher else ""
|
|
218
|
+
citation += f' Version {version}. {publisher}{item_type}. https://doi.org/{doi}'
|
|
219
|
+
return citation
|
|
220
|
+
except TypeError:
|
|
221
|
+
logging.error('could not make citation for %s', doi)
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
def custom_field_name (name):
|
|
225
|
+
"""Return a predictable name for a custom field."""
|
|
226
|
+
|
|
227
|
+
name = name.lower().replace(" ", "_")
|
|
228
|
+
|
|
229
|
+
## Exceptions to the custom field names.
|
|
230
|
+
if name == "licence_remarks":
|
|
231
|
+
name = "license_remarks"
|
|
232
|
+
if name == "geolocation_latitude":
|
|
233
|
+
name = "latitude"
|
|
234
|
+
if name == "geolocation_longitude":
|
|
235
|
+
name = "longitude"
|
|
236
|
+
|
|
237
|
+
return name
|
|
238
|
+
|
|
239
|
+
def is_opendap_url (url):
|
|
240
|
+
"""Returns True when URL links to 4TU's OPeNDAP server, False otherwise."""
|
|
241
|
+
try:
|
|
242
|
+
return url.split("/")[2] == "opendap.4tu.nl"
|
|
243
|
+
except (AttributeError, IndexError, KeyError):
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
def add_logging_level (level_name, level_number, method_name=None):
|
|
247
|
+
"""
|
|
248
|
+
Comprehensively adds a new logging level to the `logging` module and the
|
|
249
|
+
currently configured logging class.
|
|
250
|
+
|
|
251
|
+
`level_name` becomes an attribute of the `logging` module with the value
|
|
252
|
+
`level_number`. `method_name` becomes a convenience method for both `logging`
|
|
253
|
+
itself and the class returned by `logging.getLoggerClass()` (usually just
|
|
254
|
+
`logging.Logger`). If `method_name` is not specified, `level_name.lower()` is
|
|
255
|
+
used.
|
|
256
|
+
|
|
257
|
+
To avoid accidental clobberings of existing attributes, this method will
|
|
258
|
+
not overwrite existing attributes.
|
|
259
|
+
|
|
260
|
+
Code copied and adapted from the following Stack Overflow post:
|
|
261
|
+
https://stackoverflow.com/questions/2183233/35804945#35804945
|
|
262
|
+
"""
|
|
263
|
+
if not method_name:
|
|
264
|
+
method_name = level_name.lower()
|
|
265
|
+
|
|
266
|
+
if (hasattr(logging, level_name) or
|
|
267
|
+
hasattr(logging, method_name) or
|
|
268
|
+
hasattr(logging.getLoggerClass(), method_name)):
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
# This method was inspired by the answers to Stack Overflow post
|
|
272
|
+
# http://stackoverflow.com/q/2183233/2988730, especially
|
|
273
|
+
# http://stackoverflow.com/a/13638084/2988730
|
|
274
|
+
def log_for_level(self, message, *args, **kwargs):
|
|
275
|
+
if self.isEnabledFor(level_number):
|
|
276
|
+
self._log(level_number, message, args, **kwargs) # pylint: disable=protected-access
|
|
277
|
+
|
|
278
|
+
def log_to_root(message, *args, **kwargs):
|
|
279
|
+
logging.log(level_number, message, *args, **kwargs)
|
|
280
|
+
|
|
281
|
+
logging.addLevelName(level_number, level_name)
|
|
282
|
+
setattr(logging, level_name, level_number)
|
|
283
|
+
setattr(logging.getLoggerClass(), method_name, log_for_level)
|
|
284
|
+
setattr(logging, method_name, log_to_root)
|
|
285
|
+
|
|
286
|
+
def landing_page_url (item_id, version=None, item_type="dataset", base_url=""):
|
|
287
|
+
"""Returns (a version-specific) URL to an item's landing page."""
|
|
288
|
+
url = f"{base_url}/{item_type}s/{item_id}"
|
|
289
|
+
if version:
|
|
290
|
+
url += f"/{version}"
|
|
291
|
+
return url
|
|
292
|
+
|
|
293
|
+
def split_author_name(name):
|
|
294
|
+
""" Procedure to split name of author into first and last name.
|
|
295
|
+
Works for often occurring name patterns:
|
|
296
|
+
* first_name (initials) last_name
|
|
297
|
+
* first_name I.N.I.T. last_name
|
|
298
|
+
* first_name last_name
|
|
299
|
+
* last_name """
|
|
300
|
+
name = re.sub(r'\s+', ' ', name)
|
|
301
|
+
if ')' in name:
|
|
302
|
+
parts = name.split(')', 1)
|
|
303
|
+
parts[0] += ')'
|
|
304
|
+
elif '.' in name:
|
|
305
|
+
parts = name[::-1].split('.', 1)
|
|
306
|
+
parts = [part[::-1] for part in parts][::-1]
|
|
307
|
+
parts[0] += '.'
|
|
308
|
+
else:
|
|
309
|
+
parts = name.split(' ', 1)
|
|
310
|
+
parts = [part.strip() for part in parts]
|
|
311
|
+
parts = ([''] + parts)[-2:]
|
|
312
|
+
return parts
|
|
313
|
+
|
|
314
|
+
def split_string (input_string, delimiter=","):
|
|
315
|
+
"""Splits a string by a delimiter character and strips whitespace."""
|
|
316
|
+
if not isinstance (input_string, str) or input_string == "":
|
|
317
|
+
return None
|
|
318
|
+
if not isinstance (delimiter, str) or len(delimiter) != 1:
|
|
319
|
+
return None
|
|
320
|
+
|
|
321
|
+
parts = [part.strip() for part in input_string.split (delimiter)]
|
|
322
|
+
return [part for part in parts if part]
|
|
323
|
+
|
|
324
|
+
def strip_string (input_string):
|
|
325
|
+
"""Removes whitespace from the beginning and end of a string."""
|
|
326
|
+
if isinstance (input_string, str):
|
|
327
|
+
return input_string.strip()
|
|
328
|
+
return input_string
|
|
329
|
+
|
|
330
|
+
def normalize_identifier (value, prefix_url):
|
|
331
|
+
"""Procedure to rewrite URLs to URIs."""
|
|
332
|
+
# Don't process invalid entries
|
|
333
|
+
if not isinstance(value, str):
|
|
334
|
+
return None
|
|
335
|
+
# Don't store empty values.
|
|
336
|
+
value = value.strip()
|
|
337
|
+
if value == "":
|
|
338
|
+
return None
|
|
339
|
+
# Strip the URI prefix from identifiers.
|
|
340
|
+
if value.startswith (prefix_url):
|
|
341
|
+
return value[len(prefix_url):]
|
|
342
|
+
|
|
343
|
+
return value
|
|
344
|
+
|
|
345
|
+
def normalize_orcid (orcid):
|
|
346
|
+
"""Procedure to make storing ORCID identifiers consistent."""
|
|
347
|
+
return normalize_identifier (orcid, "https://orcid.org/")
|
|
348
|
+
|
|
349
|
+
def normalize_doi (doi):
|
|
350
|
+
"""Procedure to make storing DOIs consistent."""
|
|
351
|
+
return normalize_identifier (normalize_identifier (doi, "https://doi.org/"), "doi.org/")
|
|
352
|
+
|
|
353
|
+
def limit_memory_for_subprocess (limit_in_bytes = 4096000000):
|
|
354
|
+
"""Procedure to attempt to limit the memory usage of a subprocess."""
|
|
355
|
+
if not RLIMIT_AVAILABLE:
|
|
356
|
+
logging.warning ("Because 'setrlimit' is not available, memory usage may rise unbounded.")
|
|
357
|
+
return None
|
|
358
|
+
|
|
359
|
+
try:
|
|
360
|
+
setrlimit (RLIMIT_AS, (limit_in_bytes, RLIM_INFINITY))
|
|
361
|
+
except ValueError:
|
|
362
|
+
logging.warning ("Call to 'setrlimit' failed.")
|
|
363
|
+
|
|
364
|
+
return None
|