You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2025/08/18 15:23:52 UTC
(allura) 04/04: [#8520] make a pkg_file helper for shorter and safer file locating code
This is an automated email from the ASF dual-hosted git repository.
brondsem pushed a commit to branch db/8520
in repository https://gitbox.apache.org/repos/asf/allura.git
commit 1a233df85759b48e776705a7740abb922c114fff
Author: Dave Brondsema <db...@slashdotmedia.com>
AuthorDate: Fri Aug 15 18:16:25 2025 -0400
[#8520] make a pkg_file helper for shorter and safer file locating code
---
Allura/allura/app.py | 5 ++--
Allura/allura/config/middleware.py | 4 +--
Allura/allura/config/resources.py | 11 ++++---
Allura/allura/ext/admin/admin_main.py | 4 +--
Allura/allura/ext/project_home/project_main.py | 5 ++--
Allura/allura/ext/search/search_main.py | 4 +--
Allura/allura/ext/user_profile/user_main.py | 5 ++--
Allura/allura/lib/app_globals.py | 5 ++--
Allura/allura/lib/custom_middleware.py | 7 ++---
Allura/allura/lib/package_path_loader.py | 11 +++----
Allura/allura/lib/plugin.py | 5 ++--
Allura/allura/lib/utils.py | 22 ++++++++++++++
.../tests/functional/test_personal_dashboard.py | 3 +-
Allura/allura/tests/test_commands.py | 6 ++--
Allura/allura/tests/test_utils.py | 20 +++++++++++++
.../allura/tests/unit/test_package_path_loader.py | 4 +--
ForgeActivity/forgeactivity/config/resources.py | 4 +--
.../forgediscussion/tests/functional/test_forum.py | 5 ++--
ForgeFiles/forgefiles/files_main.py | 2 +-
.../forgegit/tests/functional/test_controllers.py | 17 +++++------
ForgeGit/forgegit/tests/model/test_repository.py | 35 +++++++++++-----------
.../forgesvn/tests/functional/test_controllers.py | 6 ++--
ForgeSVN/forgesvn/tests/model/test_repository.py | 20 ++++++-------
ForgeTracker/forgetracker/config/resources.py | 6 ++--
ForgeUserStats/forgeuserstats/tests/test_model.py | 4 +--
ForgeUserStats/forgeuserstats/tests/test_stats.py | 5 ++--
26 files changed, 133 insertions(+), 92 deletions(-)
diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index 1d1723a7a..ac602fe50 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -23,7 +23,6 @@
from xml.etree import ElementTree as ET
from copy import copy
-import importlib.resources
from markupsafe import Markup
from tg import expose, redirect, flash, validate
from tg.decorators import without_trailing_slash
@@ -45,7 +44,7 @@
from allura import model
from allura.controllers import BaseController
from allura.lib.decorators import require_post, memoize
-from allura.lib.utils import permanent_redirect, ConfigProxy
+from allura.lib.utils import permanent_redirect, ConfigProxy, pkg_file
from allura import model as M
from allura.tasks import index_tasks
import six
@@ -475,7 +474,7 @@ def has_resource(cls, resource_path):
"""
for klass in [o for o in cls.__mro__ if issubclass(o, Application)]:
package = klass.__module__.split(".")[0]
- if importlib.resources.files(package).joinpath(resource_path).exists():
+ if os.path.exists(pkg_file(package, resource_path)):
return klass
def has_access(self, user, topic):
diff --git a/Allura/allura/config/middleware.py b/Allura/allura/config/middleware.py
index 214579ed7..d1cd4cdff 100644
--- a/Allura/allura/config/middleware.py
+++ b/Allura/allura/config/middleware.py
@@ -65,7 +65,7 @@
from allura.lib.custom_middleware import SetHeadersMiddleware
from allura.lib.custom_middleware import StatusCodeRedirect
from allura.lib import helpers as h
-from allura.lib.utils import configure_ming
+from allura.lib.utils import configure_ming, pkg_file
__all__ = ['make_app']
@@ -108,7 +108,7 @@ def _make_core_app(root, global_conf: dict, **app_conf):
under ``[app:main]``.
"""
# Run all the initialization code here
- mimetypes.init([str(importlib.resources.files('allura')), 'etc/mime.types'] + mimetypes.knownfiles)
+ mimetypes.init([pkg_file('allura', 'etc/mime.types')] + mimetypes.knownfiles)
# Configure MongoDB
configure_ming(app_conf)
diff --git a/Allura/allura/config/resources.py b/Allura/allura/config/resources.py
index 0693d1746..8fac055c2 100644
--- a/Allura/allura/config/resources.py
+++ b/Allura/allura/config/resources.py
@@ -18,20 +18,19 @@
import os
import logging
-import importlib.resources
-
from allura.lib.helpers import iter_entry_points
+from allura.lib.utils import pkg_file
log = logging.getLogger(__name__)
def register_ew_resources(manager):
manager.register_directory(
- 'js', str(importlib.resources.files('allura').joinpath('lib/widgets/resources/js')))
+ 'js', pkg_file('allura', 'lib/widgets/resources/js'))
manager.register_directory(
- 'css', str(importlib.resources.files('allura').joinpath('lib/widgets/resources/css')))
+ 'css', pkg_file('allura', 'lib/widgets/resources/css'))
manager.register_directory(
- 'allura', str(importlib.resources.files('allura').joinpath('public/nf')))
+ 'allura', pkg_file('allura', 'public/nf'))
for ep in iter_entry_points('allura'):
try:
app = ep.load()
@@ -41,7 +40,7 @@ def register_ew_resources(manager):
package = resource_cls.__module__.split(".")[0]
manager.register_directory(
'tool/%s' % ep.name.lower(),
- importlib.resources.files(package).joinpath(resource_path))
+ pkg_file(package, resource_path))
except ImportError:
log.warning('Cannot import entry point %s', ep)
raise
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index f055ba2c0..aea3085b9 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -23,7 +23,6 @@
from urllib.parse import urlparse
import json
from operator import itemgetter, attrgetter
-import importlib.resources
from tg import tmpl_context as c, app_globals as g
from tg import request
@@ -47,6 +46,7 @@
from allura.lib.decorators import require_post
from allura.tasks import export_tasks
from allura.lib.widgets.project_list import ProjectScreenshots
+from allura.lib.utils import pkg_file
from . import widgets as aw
import six
@@ -89,7 +89,7 @@ def __init__(self, project, config):
self.root = ProjectAdminController()
self.api_root = ProjectAdminRestController()
self.admin = AdminAppAdminController(self)
- self.templates = str(importlib.resources.files('allura.ext.admin')) + '/templates'
+ self.templates = pkg_file('allura.ext.admin', 'templates')
self.sitemap = [SitemapEntry('Admin', '.')]
def is_visible_to(self, user):
diff --git a/Allura/allura/ext/project_home/project_main.py b/Allura/allura/ext/project_home/project_main.py
index cfc135cf8..e015f254c 100644
--- a/Allura/allura/ext/project_home/project_main.py
+++ b/Allura/allura/ext/project_home/project_main.py
@@ -17,7 +17,6 @@
import logging
-import importlib.resources
from tg import tmpl_context as c
from tg import expose, redirect
from tg.decorators import with_trailing_slash
@@ -25,10 +24,10 @@
from allura import version
from allura.app import Application, SitemapEntry
from allura.lib import helpers as h
+from allura.lib.utils import pkg_file
from allura.controllers import BaseController
from allura import model
-
log = logging.getLogger(__name__)
@@ -46,7 +45,7 @@ class ProjectHomeApp(Application):
def __init__(self, project, config):
Application.__init__(self, project, config)
self.root = ProjectHomeController()
- self.templates = str(importlib.resources.files('allura.ext.project_home') / 'templates')
+ self.templates = pkg_file('allura.ext.project_home', 'templates')
def is_visible_to(self, user):
'''Whether the user can view the app.'''
diff --git a/Allura/allura/ext/search/search_main.py b/Allura/allura/ext/search/search_main.py
index baf5a8a71..72c0c58e0 100644
--- a/Allura/allura/ext/search/search_main.py
+++ b/Allura/allura/ext/search/search_main.py
@@ -17,7 +17,6 @@
import logging
-import importlib.resources
from tg import tmpl_context as c
from tg import expose, validate
from tg.decorators import with_trailing_slash
@@ -26,6 +25,7 @@
from allura.app import Application
from allura import version
from allura.lib.search import search_app
+from allura.lib.utils import pkg_file
from allura.lib.widgets.search import SearchResults, SearchHelp
from allura.controllers import BaseController
@@ -46,7 +46,7 @@ class SearchApp(Application):
def __init__(self, project, config):
Application.__init__(self, project, config)
self.root = SearchController()
- self.templates = str(importlib.resources.files('allura.ext.search') / 'templates')
+ self.templates = pkg_file('allura.ext.search', 'templates')
def main_menu(self): # pragma no cover
return []
diff --git a/Allura/allura/ext/user_profile/user_main.py b/Allura/allura/ext/user_profile/user_main.py
index 6ae5a8676..416436dfe 100644
--- a/Allura/allura/ext/user_profile/user_main.py
+++ b/Allura/allura/ext/user_profile/user_main.py
@@ -16,8 +16,6 @@
# under the License.
import logging
-
-import importlib.resources
from datetime import datetime
import six
@@ -38,6 +36,7 @@
from allura.lib.decorators import require_post
from allura.lib.plugin import AuthenticationProvider
from allura.lib.security import require_access
+from allura.lib.utils import pkg_file
from allura.lib.widgets.user_profile import SendMessageForm, SectionsUtil, SectionBase, ProjectsSectionBase
from allura.model import ACE, ProjectRole
@@ -67,7 +66,7 @@ class UserProfileApp(Application):
def __init__(self, user, config):
Application.__init__(self, user, config)
self.root = UserProfileController()
- self.templates = str(importlib.resources.files('allura.ext.user_profile') / 'templates')
+ self.templates = pkg_file('allura.ext.user_profile', 'templates')
self.api_root = UserProfileRestController()
@property
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index 47e4eded4..23c6fd494 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -25,7 +25,6 @@
import os
import time
import traceback
-import importlib.resources
import activitystream
import markdown
@@ -38,6 +37,7 @@
from tg import tmpl_context as c
from paste.deploy.converters import asbool, asint, aslist
from pypeline.markup import markup as pypeline_markup
+
from ming.odm import session, MappedClass
import ew as ew_core
@@ -53,6 +53,7 @@
EmojiExtension,
UserMentionExtension
)
+from allura.lib.utils import pkg_file
from allura.eventslistener import PostEvent
from allura.lib import gravatar, plugin, utils
@@ -194,7 +195,7 @@ def __init__(self):
self.__dict__ = self.__shared_state
if self.__shared_state:
return
- self.allura_templates = str(importlib.resources.files('allura') / 'templates')
+ self.allura_templates = pkg_file('allura', 'templates')
# Setup SOLR
self.solr_server = aslist(config.get('solr.server'), ',')
# skip empty strings in case of extra commas
diff --git a/Allura/allura/lib/custom_middleware.py b/Allura/allura/lib/custom_middleware.py
index 685b231a1..a2c672b9e 100644
--- a/Allura/allura/lib/custom_middleware.py
+++ b/Allura/allura/lib/custom_middleware.py
@@ -20,7 +20,6 @@
import logging
import tg
-import importlib.resources
from paste import fileapp
from paste.deploy.converters import aslist, asbool
from tg import tmpl_context as c
@@ -31,7 +30,7 @@
from ming.odm import session
from allura.lib import helpers as h
-from allura.lib.utils import is_ajax
+from allura.lib.utils import is_ajax, pkg_file
from allura import model as M
import allura.model.repository
from tg import tmpl_context as c
@@ -79,10 +78,10 @@ def get_app(self, environ):
resource_cls = ep.load().has_resource(resource_path)
if resource_cls:
package = resource_cls.__module__.split(".")[0]
- file_path = str(importlib.resources.files(package) / resource_path)
+ file_path = pkg_file(package, resource_path)
return fileapp.FileApp(file_path, [('Access-Control-Allow-Origin', '*')])
filename = environ['PATH_INFO'][len(self.script_name):]
- file_path = str(importlib.resources.files('allura')) + '/public/nf/' + filename
+ file_path = pkg_file('allura', 'public/nf', filename)
return fileapp.FileApp(file_path, [('Access-Control-Allow-Origin', '*')])
diff --git a/Allura/allura/lib/package_path_loader.py b/Allura/allura/lib/package_path_loader.py
index 6fe11d163..a9d63200a 100644
--- a/Allura/allura/lib/package_path_loader.py
+++ b/Allura/allura/lib/package_path_loader.py
@@ -122,12 +122,13 @@ class NewToolApp(Application):
**TODO:** Support multiple partial themes
"""
-import importlib.resources
import os
import jinja2
from tg import config
from paste.deploy.converters import asbool
+
+from allura.lib.utils import pkg_file
from ming.utils import LazyProperty
from allura.lib.helpers import topological_sort, iter_entry_points
@@ -164,9 +165,9 @@ def _load_paths(self):
"""
paths = self.default_paths[:] # copy default_paths
paths[-1:0] = [ # insert all eps just before last item, by default
- [ep.name, str(
- importlib.resources.files(ep.module.split(".")[0]) # first part gives a pkg, for importlib
- / '/'.join(ep.module.split(".")[1:-1]) # append the rest (except last part) to match pkg_resources previous behavior. A bit weird.
+ [ep.name, pkg_file(
+ ep.module.split(".")[0], # first part gives a pkg, for importlib
+ *ep.module.split(".")[1:-1] # append the rest (except last part) to match pkg_resources previous behavior. A bit weird.
)]
for ep in iter_entry_points(self.override_entrypoint)
]
@@ -281,7 +282,7 @@ def get_source(self, environment, template):
if ':' in template:
package, path = template.split(':', 2)
- filename = str(importlib.resources.files(package) / path)
+ filename = pkg_file(package, path)
return self.fs_loader.get_source(environment, filename)
else:
return self.fs_loader.get_source(environment, template)
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index b327ac79c..0ee13d737 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -40,12 +40,13 @@
import six
from passlib.context import CryptContext
+from allura.lib.utils import pkg_file
+
try:
import ldap
from ldap import modlist
except ImportError:
ldap = modlist = None
-import importlib.resources
import tg
from tg import config, request, redirect, response
from tg import tmpl_context as c, app_globals as g
@@ -1471,7 +1472,7 @@ def require(self):
def register_ew_resources(cls, manager, name):
manager.register_directory(
'theme/%s' % name,
- str(importlib.resources.files('allura')) + '/nf/' + name)
+ pkg_file('allura', 'nf/' + name))
def href(self, href, theme_name=None):
'''
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index 0391f026c..6ebb95c80 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -17,6 +17,7 @@
from __future__ import annotations
import base64
+import importlib
from collections.abc import Iterable, Mapping, MutableMapping
from contextlib import contextmanager
import time
@@ -28,6 +29,7 @@
import random
import mimetypes
import re
+from pathlib import Path
from typing import TypeVar
import magic
from itertools import groupby
@@ -820,3 +822,23 @@ def socket_default_timeout(timeout):
yield
finally:
socket.setdefaulttimeout(orig_timeout)
+
+
+def join_paths_no_traversal(base: str | Path, *paths: str | Path) -> str:
+ for p in paths:
+ if Path(p).is_absolute():
+ raise ValueError(f"Absolute paths is not allowed: {p}")
+ if any(part == ".." for part in Path(p).parts):
+ raise ValueError(f"Parent directory traversal is not allowed: {p}")
+ base = Path(base)
+ joined = str(base.joinpath(*paths).resolve())
+ base_resolved = str(base.resolve())
+ if not joined.startswith(base_resolved):
+ raise ValueError("Resulting path escapes base directory")
+ if paths and str(paths[-1]).endswith('/'):
+ joined += '/'
+ return joined
+
+
+def pkg_file(pkg_name: str, *paths: str) -> str:
+ return join_paths_no_traversal(importlib.resources.files(pkg_name), *paths)
diff --git a/Allura/allura/tests/functional/test_personal_dashboard.py b/Allura/allura/tests/functional/test_personal_dashboard.py
index 59b8e44b5..5109477a9 100644
--- a/Allura/allura/tests/functional/test_personal_dashboard.py
+++ b/Allura/allura/tests/functional/test_personal_dashboard.py
@@ -19,6 +19,7 @@
import mock
import tg
+from allura.lib.utils import pkg_file
from ming.odm import ThreadLocalODMSession, ThreadLocalODMSession
from tg import tmpl_context as c
@@ -95,7 +96,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test2', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.name = 'testgit.git'
self.repo = c.app.repo
diff --git a/Allura/allura/tests/test_commands.py b/Allura/allura/tests/test_commands.py
index 52ef9aea8..112557e42 100644
--- a/Allura/allura/tests/test_commands.py
+++ b/Allura/allura/tests/test_commands.py
@@ -20,6 +20,7 @@
from testfixtures import OutputCapture
+from allura.lib.utils import pkg_file
from ming.base import Object
from ming.odm import ThreadLocalODMSession
from mock import Mock, call, patch
@@ -51,10 +52,9 @@ def setup_module():
def test_script():
cmd = script.ScriptCommand('script')
- cmd.run(
- [test_config, str(importlib.resources.files('allura') / 'tests/tscript.py')])
+ cmd.run([test_config, pkg_file('allura', 'tests/tscript.py')])
with pytest.raises(ValueError):
- cmd.run([test_config, str(importlib.resources.files('allura') / 'tests/tscript_error.py')])
+ cmd.run([test_config, pkg_file('allura', 'tests/tscript_error.py')])
def test_set_neighborhood_max_projects():
diff --git a/Allura/allura/tests/test_utils.py b/Allura/allura/tests/test_utils.py
index 64d02c59b..a47950d53 100644
--- a/Allura/allura/tests/test_utils.py
+++ b/Allura/allura/tests/test_utils.py
@@ -19,6 +19,7 @@
import time
import datetime as dt
from os import path
+from pathlib import Path
import ming
from ming.odm import session
@@ -409,3 +410,22 @@ def test_urlencode():
# list of pairs - including unicode and bytes
assert (utils.urlencode([('a', 1), ('b', 'ƒ'), ('c', 'ƒ'.encode())]) ==
'a=1&b=%C6%92&c=%C6%92')
+
+
+@pytest.mark.parametrize('base, paths, expected', [
+ ('/foo', ['bar'], '/foo/bar'),
+ ('/foo', ['bar/'], '/foo/bar/'),
+ ('/foo', ['bar', 'baz'], '/foo/bar/baz'),
+ (Path('/foo'), [Path('bar')], '/foo/bar'),
+ ('/foo', ['/tmp'], ValueError),
+ ('/foo', ['x', '/tmp'], ValueError),
+ ('/foo', ['..'], ValueError),
+ ('/foo', ['bar/..'], ValueError),
+ ('/foo', ['x', 'bar/../asf'], ValueError),
+])
+def test_join_paths_no_traversal(base, paths, expected: str | type[Exception]):
+ if isinstance(expected, str):
+ assert utils.join_paths_no_traversal(base, *paths) == expected
+ else:
+ with pytest.raises(expected):
+ utils.join_paths_no_traversal(base, *paths)
diff --git a/Allura/allura/tests/unit/test_package_path_loader.py b/Allura/allura/tests/unit/test_package_path_loader.py
index bbff977fc..3d9383843 100644
--- a/Allura/allura/tests/unit/test_package_path_loader.py
+++ b/Allura/allura/tests/unit/test_package_path_loader.py
@@ -189,7 +189,7 @@ def test_get_source(self, fs_loader):
jinja2.TemplateNotFound('test'), 'fs_load']
with mock.patch('importlib.resources.files') as rf:
- rf.return_value = PurePath('resource')
+ rf.return_value = PurePath('/resource')
# no override, ':' in template
output = ppl.get_source(
'env', 'allura.ext.admin:templates/audit.html')
@@ -197,7 +197,7 @@ def test_get_source(self, fs_loader):
assert output == 'fs_load'
assert fs_loader().get_source.call_count == 2
- fs_loader().get_source.assert_called_with('env', 'resource/templates/audit.html')
+ fs_loader().get_source.assert_called_with('env', '/resource/templates/audit.html')
fs_loader().get_source.reset_mock()
fs_loader().get_source.side_effect = [
diff --git a/ForgeActivity/forgeactivity/config/resources.py b/ForgeActivity/forgeactivity/config/resources.py
index 303571713..6aa193936 100644
--- a/ForgeActivity/forgeactivity/config/resources.py
+++ b/ForgeActivity/forgeactivity/config/resources.py
@@ -15,9 +15,9 @@
# specific language governing permissions and limitations
# under the License.
-import importlib.resources
+from allura.lib.utils import pkg_file
def register_ew_resources(manager):
manager.register_directory(
- 'activity_js', importlib.resources.files('forgeactivity') / 'widgets/resources/js')
+ 'activity_js', pkg_file('forgeactivity', 'widgets/resources/js'))
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
index 78909240e..55dc9db5e 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
@@ -15,7 +15,6 @@
# specific language governing permissions and limitations
# under the License.
-import os
import mock
import pytest
import random
@@ -24,7 +23,6 @@
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
-import importlib.resources
import pymongo
from ming.odm import ThreadLocalODMSession
@@ -38,6 +36,7 @@
from allura.tasks import mail_tasks
from alluratest.controller import TestController
from allura.lib import helpers as h
+from allura.lib.utils import pkg_file
from allura.tests import decorators as td
from forgediscussion import model as FM
@@ -104,7 +103,7 @@ def test_html_email_with_images(self):
],
policy=email_policy
)
- with open(str(importlib.resources.files('forgediscussion')) + '/tests/data/python-logo.png', 'rb') as fp:
+ with open(pkg_file('forgediscussion', 'tests/data/python-logo.png'), 'rb') as fp:
img = MIMEImage(fp.read(), policy=email_policy)
img.add_header('Content-Disposition', 'attachment',
filename='python-logo.png')
diff --git a/ForgeFiles/forgefiles/files_main.py b/ForgeFiles/forgefiles/files_main.py
index 9279014e1..21ad9b12b 100755
--- a/ForgeFiles/forgefiles/files_main.py
+++ b/ForgeFiles/forgefiles/files_main.py
@@ -368,7 +368,7 @@ def publish_folder(self, folder_id=None, remarks=None):
try:
for i in user_ids:
user_object = M.User.query.get(_id=i)
- template_name = 'forgefiles:/templates/mail.html'
+ template_name = 'forgefiles:templates/mail.html'
text = g.jinja2_env.get_template(template_name).render(dict(
base_url=config.get('base_url'), user_object=user_object, project=c.project,
remarks=remarks, folder_object=folder_object, project_owner=c.user,
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py b/ForgeGit/forgegit/tests/functional/test_controllers.py
index 5505d2bb6..b0e74de78 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -14,7 +14,6 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-import importlib.resources
import json
import re
import os
@@ -22,7 +21,6 @@
import tempfile
import textwrap
-import importlib.resources
from alluratest.tools import assert_regexp_matches
from tg import tmpl_context as c
import tg
@@ -33,6 +31,7 @@
from allura import model as M
from allura.lib import helpers as h
from allura.lib import macro
+from allura.lib.utils import pkg_file
from alluratest.controller import TestController, TestRestApiBase
from allura.tests.decorators import with_tool
from allura.tests.test_globals import squish_spaces
@@ -49,7 +48,7 @@ def setup_method(self, method):
@with_git
def setup_with_tools(self):
h.set_context('test', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.status = 'ready'
c.app.repo.name = 'testgit.git'
@@ -65,7 +64,7 @@ def setup_with_tools(self):
@with_tool('test', 'Git', 'testgit-index', 'Git', type='git')
def setup_testgit_index_repo(self):
h.set_context('test', 'testgit-index', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.status = 'ready'
c.app.repo.name = 'testgit_index.git'
@@ -83,7 +82,7 @@ def setup_method(self, method):
@with_git
def setup_with_tools(self):
h.set_context('test', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.name = 'testui2.git'
c.app.repo.status = 'ready'
@@ -114,7 +113,7 @@ class TestRootController(_TestCase):
@with_tool('test', 'Git', 'weird-chars', 'WeirdChars', type='git')
def _setup_weird_chars_repo(self):
h.set_context('test', 'weird-chars', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.status = 'ready'
c.app.repo.name = 'weird-chars.git'
@@ -978,7 +977,7 @@ def setup_method(self, method):
@with_git
def setup_with_tools(self):
h.set_context('test', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.status = 'ready'
c.app.repo.name = 'testmime.git'
@@ -1007,7 +1006,7 @@ def setup_method(self, method):
@with_git
def setup_with_tools(self):
h.set_context('test', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.status = 'ready'
c.app.repo.name = 'testrename.git'
@@ -1075,7 +1074,7 @@ def setup_method(self, method):
@with_git
def setup_with_tools(self):
h.set_context('test', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.status = 'ready'
c.app.repo.name = 'test_branch.git'
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index 7d8628067..b4e57a051 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -18,13 +18,14 @@
import os
import shutil
import stat
-import importlib.resources
import datetime
import email.iterators
import pytest
import mock
from tg import tmpl_context as c, app_globals as g
import tg
+
+from allura.lib.utils import pkg_file
from ming.base import Object
from ming.odm import ThreadLocalODMSession, session
from testfixtures import TempDirectory
@@ -54,7 +55,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.name = 'testgit.git'
self.repo = c.app.repo
@@ -130,7 +131,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.name = 'testgit.git'
self.repo = c.app.repo
@@ -166,7 +167,7 @@ def test_fork(self):
url_path='/test/',
tool='git',
status='creating')
- repo_path = str(importlib.resources.files('forgegit') / 'tests/data/testgit.git')
+ repo_path = pkg_file('forgegit', 'tests/data/testgit.git')
dirname = os.path.join(repo.fs_path, repo.name)
if os.path.exists(dirname):
shutil.rmtree(dirname)
@@ -188,7 +189,7 @@ def test_clone(self):
url_path='/test/',
tool='git',
status='creating')
- repo_path = str(importlib.resources.files('forgegit') / 'tests/data/testgit.git')
+ repo_path = pkg_file('forgegit', 'tests/data/testgit.git')
dirname = os.path.join(repo.fs_path, repo.name)
if os.path.exists(dirname):
shutil.rmtree(dirname)
@@ -220,7 +221,7 @@ def test_hotcopy(self, clone_from):
tool='git',
status='creating')
repo.app.config.options['hotcopy'] = True
- repo_path = str(importlib.resources.files('forgegit') / 'tests/data/testgit.git')
+ repo_path = pkg_file('forgegit', 'tests/data/testgit.git')
dirname = os.path.join(repo.fs_path, repo.name)
if os.path.exists(dirname):
shutil.rmtree(dirname)
@@ -429,7 +430,7 @@ def test_notification_email_max_commits(self):
@td.with_tool('test', 'Git', 'weird-chars', 'WeirdChars', type='git')
def _setup_weird_chars_repo(self):
h.set_context('test', 'weird-chars', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.status = 'ready'
c.app.repo.name = 'weird-chars.git'
@@ -600,7 +601,7 @@ def test_default_branch_set(self):
assert self.repo.get_default_branch(('main', 'master')) == 'zz'
def test_update_default_branch(self):
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data/testgit.git')
+ repo_dir = pkg_file('forgegit', 'tests/data/testgit.git')
repo = mock.Mock(full_fs_path=repo_dir)
repo.__ming__ = mock.Mock()
impl = GM.git_repo.GitImplementation(repo)
@@ -793,7 +794,7 @@ def test_merge_raise_exception(self, git, shutil, tempfile):
def test_paged_diffs(self):
# setup
h.set_context('test', 'src-weird', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
repo = GM.Repository(
name='weird-chars.git',
fs_path=repo_dir,
@@ -896,7 +897,7 @@ def test_paged_diffs(self):
def test_paged_diffs_with_detect_copies(self):
# setup
h.set_context('test', 'src-weird', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
repo = GM.Repository(
name='weird-chars.git',
fs_path=repo_dir,
@@ -979,7 +980,7 @@ def test_cached_tags(self):
class TestGitImplementation:
def test_branches(self):
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data/testgit.git')
+ repo_dir = pkg_file('forgegit', 'tests/data/testgit.git')
repo = mock.Mock(full_fs_path=repo_dir)
repo.__ming__ = mock.Mock()
repo.cached_branches = []
@@ -992,7 +993,7 @@ def test_branches(self):
]
def test_tags(self):
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data/testgit.git')
+ repo_dir = pkg_file('forgegit', 'tests/data/testgit.git')
repo = mock.Mock(full_fs_path=repo_dir)
repo.__ming__ = mock.Mock()
repo.cached_tags = []
@@ -1003,7 +1004,7 @@ def test_tags(self):
]
def test_last_commit_ids(self):
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data/testrename.git')
+ repo_dir = pkg_file('forgegit', 'tests/data/testrename.git')
repo = mock.Mock(full_fs_path=repo_dir)
impl = GM.git_repo.GitImplementation(repo)
def lcd(c, p): return impl.last_commit_ids(mock.Mock(_id=c), p)
@@ -1022,7 +1023,7 @@ def test_last_commit_ids_threaded(self):
@mock.patch('forgegit.model.git_repo.GitImplementation._git', new_callable=mock.PropertyMock)
def test_last_commit_ids_threaded_error(self, _git):
with h.push_config(tg.config, lcd_thread_chunk_size=1, lcd_timeout=2):
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data/testrename.git')
+ repo_dir = pkg_file('forgegit', 'tests/data/testrename.git')
repo = mock.Mock(full_fs_path=repo_dir)
_git.side_effect = ValueError
impl = GM.git_repo.GitImplementation(repo)
@@ -1041,7 +1042,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.name = 'testgit.git'
self.repo = c.app.repo
@@ -1116,7 +1117,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.name = 'testmime.git'
self.repo = c.app.repo
@@ -1146,7 +1147,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test', 'src-git', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgegit') / 'tests/data')
+ repo_dir = pkg_file('forgegit', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.name = 'testrename.git'
self.repo = c.app.repo
diff --git a/ForgeSVN/forgesvn/tests/functional/test_controllers.py b/ForgeSVN/forgesvn/tests/functional/test_controllers.py
index b629029fa..c81972b55 100644
--- a/ForgeSVN/forgesvn/tests/functional/test_controllers.py
+++ b/ForgeSVN/forgesvn/tests/functional/test_controllers.py
@@ -23,6 +23,8 @@
import tg
import importlib.resources
from tg import tmpl_context as c
+
+from allura.lib.utils import pkg_file
from ming.odm import ThreadLocalODMSession
from mock import patch
@@ -41,7 +43,7 @@ def setup_method(self, method):
def _make_app(self, mount_point, name):
h.set_context('test', mount_point, neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgesvn')) + '/tests/data/'
+ repo_dir = pkg_file('forgesvn', 'tests/data/')
c.app.repo.fs_path = repo_dir
c.app.repo.status = 'ready'
c.app.repo.name = name
@@ -354,7 +356,7 @@ def setup_method(self, method):
@with_svn
def setup_with_tools(self):
h.set_context('test', 'src', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgesvn')) + '/tests/data/'
+ repo_dir = pkg_file('forgesvn', 'tests/data/')
c.app.repo.fs_path = repo_dir
c.app.repo.status = 'ready'
c.app.repo.name = 'testsvn'
diff --git a/ForgeSVN/forgesvn/tests/model/test_repository.py b/ForgeSVN/forgesvn/tests/model/test_repository.py
index db9f75dc1..7d218a755 100644
--- a/ForgeSVN/forgesvn/tests/model/test_repository.py
+++ b/ForgeSVN/forgesvn/tests/model/test_repository.py
@@ -18,7 +18,6 @@
import shutil
from unittest import skipUnless
-import importlib.resources
from itertools import count, product
from datetime import datetime
from zipfile import ZipFile
@@ -38,6 +37,7 @@
from allura import model as M
from allura.model.repo_refresh import send_notifications
from allura.lib import helpers as h
+from allura.lib.utils import pkg_file
from allura.webhooks import RepoPushWebhookSender
from allura.tests.model.test_repo import RepoImplTestBase
@@ -57,7 +57,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test', 'src', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgesvn')) + '/tests/data/'
+ repo_dir = pkg_file('forgesvn', 'tests/data/')
c.app.repo.name = 'testsvn'
c.app.repo.fs_path = repo_dir
self.repo = c.app.repo
@@ -111,7 +111,7 @@ def setup_method(self, method):
@with_tool('test', 'SVN', 'svn-tags', 'SVN with tags')
def setup_with_tools(self):
setup_global_objects()
- repo_dir = str(importlib.resources.files('forgesvn')) + '/tests/data/'
+ repo_dir = pkg_file('forgesvn', 'tests/data/')
with h.push_context('test', 'src', neighborhood='Projects'):
c.app.repo.name = 'testsvn'
c.app.repo.fs_path = repo_dir
@@ -148,7 +148,7 @@ def test_fork(self):
url_path='/test/',
tool='svn',
status='creating')
- repo_path = str(importlib.resources.files('forgesvn')) + '/tests/data/testsvn'
+ repo_path = pkg_file('forgesvn', 'tests/data/testsvn/')
dirname = os.path.join(repo.fs_path, repo.name)
if os.path.exists(dirname):
shutil.rmtree(dirname)
@@ -198,7 +198,7 @@ def test_clone(self, post_event):
url_path='/test/',
tool='svn',
status='creating')
- repo_path = str(importlib.resources.files('forgesvn')) + '/tests/data/testsvn'
+ repo_path = pkg_file('forgesvn', 'tests/data/testsvn/')
dirname = os.path.join(repo.fs_path, repo.name)
if os.path.exists(dirname):
shutil.rmtree(dirname)
@@ -408,7 +408,7 @@ def test_commit(self):
assert entry.message
def test_svn_path_exists(self):
- repo_path = str(importlib.resources.files('forgesvn')) + '/tests/data/testsvn'
+ repo_path = pkg_file('forgesvn', 'tests/data/testsvn/')
assert svn_path_exists("file://%s/a" % repo_path)
assert svn_path_exists("file://%s" % repo_path)
assert not svn_path_exists("file://%s/badpath" % repo_path)
@@ -571,7 +571,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test', 'src', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgesvn')) + '/tests/data/'
+ repo_dir = pkg_file('forgesvn', 'tests/data/')
c.app.repo.name = 'testsvn'
c.app.repo.fs_path = repo_dir
self.repo = c.app.repo
@@ -618,7 +618,7 @@ def test_log(self):
def test_notification_email(self):
setup_global_objects()
h.set_context('test', 'src', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgesvn')) + '/tests/data/'
+ repo_dir = pkg_file('forgesvn', 'tests/data/')
self.repo = SM.Repository(
name='testsvn',
fs_path=repo_dir,
@@ -1017,7 +1017,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test', 'src', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgesvn')) + '/tests/data/'
+ repo_dir = pkg_file('forgesvn', 'tests/data/')
c.app.repo.name = 'testsvn-rename'
c.app.repo.fs_path = repo_dir
self.repo = c.app.repo
@@ -1052,7 +1052,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test', 'src', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgesvn')) + '/tests/data/'
+ repo_dir = pkg_file('forgesvn', 'tests/data/')
c.app.repo.name = 'testsvn'
c.app.repo.fs_path = repo_dir
self.repo = c.app.repo
diff --git a/ForgeTracker/forgetracker/config/resources.py b/ForgeTracker/forgetracker/config/resources.py
index e50b89fde..d5049351a 100644
--- a/ForgeTracker/forgetracker/config/resources.py
+++ b/ForgeTracker/forgetracker/config/resources.py
@@ -15,11 +15,11 @@
# specific language governing permissions and limitations
# under the License.
-import importlib.resources
+from allura.lib.utils import pkg_file
def register_ew_resources(manager):
manager.register_directory(
- 'tracker_js', str(importlib.resources.files('forgetracker').joinpath('widgets/resources/js')))
+ 'tracker_js', pkg_file('forgetracker', 'widgets/resources/js'))
manager.register_directory(
- 'tracker_css', str(importlib.resources.files('forgetracker').joinpath('widgets/resources/css')))
+ 'tracker_css', pkg_file('forgetracker', 'widgets/resources/css'))
diff --git a/ForgeUserStats/forgeuserstats/tests/test_model.py b/ForgeUserStats/forgeuserstats/tests/test_model.py
index 7296b1205..6fa47db9e 100644
--- a/ForgeUserStats/forgeuserstats/tests/test_model.py
+++ b/ForgeUserStats/forgeuserstats/tests/test_model.py
@@ -15,13 +15,13 @@
# specific language governing permissions and limitations
# under the License.
-import importlib.resources
from datetime import datetime, timedelta
from tg import tmpl_context as c
from tg import config
import mock
+from allura.lib.utils import pkg_file
from alluratest.controller import setup_basic_test, setup_global_objects, setup_trove_categories
from allura.tests import decorators as td
from allura.model import User, Project, TroveCategory
@@ -373,7 +373,7 @@ def test_commit_stats(self):
addr = M.EmailAddress.get(email='rcopeland@geek.net')
addr.confirmed = True
- repo_dir = str(importlib.resources.files('forgeuserstats') / 'tests/data')
+ repo_dir = pkg_file('forgeuserstats', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.name = 'testgit.git'
diff --git a/ForgeUserStats/forgeuserstats/tests/test_stats.py b/ForgeUserStats/forgeuserstats/tests/test_stats.py
index 81a4d0af0..321a98337 100644
--- a/ForgeUserStats/forgeuserstats/tests/test_stats.py
+++ b/ForgeUserStats/forgeuserstats/tests/test_stats.py
@@ -15,10 +15,9 @@
# specific language governing permissions and limitations
# under the License.
-import importlib.resources
-
from tg import tmpl_context as c
+from allura.lib.utils import pkg_file
from alluratest.controller import TestController, setup_basic_test, setup_global_objects
from allura.tests import decorators as td
from allura.lib import helpers as h
@@ -200,7 +199,7 @@ def setup_method(self, method):
def setup_with_tools(self):
setup_global_objects()
h.set_context('test', 'git-userstats-stats', neighborhood='Projects')
- repo_dir = str(importlib.resources.files('forgeuserstats') /'tests/data')
+ repo_dir = pkg_file('forgeuserstats', 'tests/data')
c.app.repo.fs_path = repo_dir
c.app.repo.name = 'testgit.git'
self.repo = c.app.repo