PR#3678: Move db/auth to kojihub module
Merges #3678 https://pagure.io/koji/pull-request/3678 Fixes: #3666 https://pagure.io/koji/issue/3666 Use koji/auth.py on hub only
This commit is contained in:
commit
32d4be1aaa
41 changed files with 137 additions and 153 deletions
|
|
@ -675,7 +675,6 @@ Here are some guidelines on producing preferable pull requests.
|
|||
|
||||
- ``cli/*``
|
||||
- ``koji/__init__.py``
|
||||
- ``koji/auth.py``
|
||||
- ``koji/tasks.py``
|
||||
- ``koji/util.py``
|
||||
- ``tests/test_lib/*``
|
||||
|
|
|
|||
|
|
@ -1834,7 +1834,7 @@ name=build
|
|||
|
||||
def get_sequence_value(cursor, sequence):
|
||||
deprecated('Function get_sequence_value will be removed in Koji 1.34. '
|
||||
'Use nextval function from koji.db.py.')
|
||||
'Use nextval function from kojihub.db.py.')
|
||||
cursor.execute("""SELECT nextval(%(sequence)s)""", locals())
|
||||
return cursor.fetchone()[0]
|
||||
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ import string
|
|||
import six
|
||||
from six.moves import range, urllib
|
||||
import koji
|
||||
from .context import context
|
||||
from .util import to_list
|
||||
from koji.context import context
|
||||
from koji.util import to_list
|
||||
|
||||
from koji.db import DeleteProcessor, InsertProcessor, QueryProcessor, UpdateProcessor, nextval
|
||||
from .db import DeleteProcessor, InsertProcessor, QueryProcessor, UpdateProcessor, nextval
|
||||
|
||||
|
||||
# 1 - load session if provided
|
||||
|
|
@ -55,8 +55,6 @@ import rpm
|
|||
from psycopg2._psycopg import IntegrityError
|
||||
|
||||
import koji
|
||||
import koji.auth
|
||||
import koji.db
|
||||
import koji.plugin
|
||||
import koji.policy
|
||||
import koji.rpmdiff
|
||||
|
|
@ -76,7 +74,8 @@ from koji.util import (
|
|||
multi_fnmatch,
|
||||
safer_move,
|
||||
)
|
||||
from koji.db import ( # noqa: F401
|
||||
from .auth import get_user_perms, get_user_groups
|
||||
from .db import ( # noqa: F401
|
||||
BulkInsertProcessor,
|
||||
DeleteProcessor,
|
||||
InsertProcessor,
|
||||
|
|
@ -1744,7 +1743,7 @@ def check_tag_access(tag_id, user_id=None):
|
|||
if user_id is None:
|
||||
raise koji.GenericError("a user_id is required")
|
||||
user_id = convert_value(user_id, cast=int)
|
||||
perms = koji.auth.get_user_perms(user_id)
|
||||
perms = get_user_perms(user_id)
|
||||
override = False
|
||||
if 'admin' in perms:
|
||||
override = True
|
||||
|
|
@ -9628,7 +9627,7 @@ class IsBuildOwnerTest(koji.policy.BaseSimpleTest):
|
|||
return True
|
||||
if owner['usertype'] == koji.USERTYPES['GROUP']:
|
||||
# owner is a group, check to see if user is a member
|
||||
if owner['id'] in koji.auth.get_user_groups(user['id']):
|
||||
if owner['id'] in get_user_groups(user['id']):
|
||||
return True
|
||||
# otherwise...
|
||||
return False
|
||||
|
|
@ -9646,7 +9645,7 @@ class UserInGroupTest(koji.policy.BaseSimpleTest):
|
|||
user = policy_get_user(data)
|
||||
if not user:
|
||||
return False
|
||||
groups = koji.auth.get_user_groups(user['id'])
|
||||
groups = get_user_groups(user['id'])
|
||||
args = self.str.split()[1:]
|
||||
for group_id, group in groups.items():
|
||||
for pattern in args:
|
||||
|
|
@ -9668,7 +9667,7 @@ class HasPermTest(koji.policy.BaseSimpleTest):
|
|||
user = policy_get_user(data)
|
||||
if not user:
|
||||
return False
|
||||
perms = koji.auth.get_user_perms(user['id'])
|
||||
perms = get_user_perms(user['id'])
|
||||
args = self.str.split()[1:]
|
||||
for perm in perms:
|
||||
for pattern in args:
|
||||
|
|
@ -9811,7 +9810,7 @@ def check_policy(name, data, default='deny', strict=False, force=False):
|
|||
logger.error("Invalid action in policy %s, rule: %s", name, lastrule)
|
||||
if force:
|
||||
user = policy_get_user(data)
|
||||
if user and 'admin' in koji.auth.get_user_perms(user['id']):
|
||||
if user and 'admin' in get_user_perms(user['id']):
|
||||
msg = "Policy %s overriden by force: %s" % (name, user["name"])
|
||||
if reason:
|
||||
msg += ": %s" % reason
|
||||
|
|
@ -12585,7 +12584,7 @@ class RootExports(object):
|
|||
values={'perm_id': perm_id})
|
||||
update.set(description=description)
|
||||
update.execute()
|
||||
if perm['name'] in koji.auth.get_user_perms(user_id):
|
||||
if perm['name'] in get_user_perms(user_id):
|
||||
raise koji.GenericError('user %s already has permission: %s' %
|
||||
(userinfo, perm['name']))
|
||||
insert = InsertProcessor('user_perms')
|
||||
|
|
@ -12599,7 +12598,7 @@ class RootExports(object):
|
|||
user_id = get_user(userinfo, strict=True)['id']
|
||||
perm = lookup_perm(permission, strict=True)
|
||||
perm_id = perm['id']
|
||||
if perm['name'] not in koji.auth.get_user_perms(user_id):
|
||||
if perm['name'] not in get_user_perms(user_id):
|
||||
raise koji.GenericError('user %s does not have permission: %s' %
|
||||
(userinfo, perm['name']))
|
||||
update = UpdateProcessor('user_perms', values=locals(),
|
||||
|
|
@ -13299,7 +13298,7 @@ class RootExports(object):
|
|||
- userID: User ID or username. If no userID provided, current login user's
|
||||
permissions will be listed."""
|
||||
user_info = get_user(userID, strict=True)
|
||||
return koji.auth.get_user_perms(user_info['id'])
|
||||
return get_user_perms(user_info['id'])
|
||||
|
||||
def getAllPerms(self):
|
||||
"""Get a list of all permissions in the system. Returns a list of maps. Each
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ import traceback
|
|||
import re
|
||||
|
||||
import koji
|
||||
import koji.auth
|
||||
import koji.db
|
||||
import koji.plugin
|
||||
import koji.policy
|
||||
import koji.util
|
||||
|
|
@ -41,6 +39,8 @@ from koji.context import context
|
|||
# import xmlrpclib functions from koji to use tweaked Marshaller
|
||||
from koji.server import ServerError, BadRequest, RequestTimeout
|
||||
from koji.xmlrpcplus import ExtendedMarshaller, Fault, dumps, getparser
|
||||
from . import auth
|
||||
from . import db
|
||||
|
||||
|
||||
class Marshaller(ExtendedMarshaller):
|
||||
|
|
@ -295,7 +295,7 @@ class ModXMLRPCRequestHandler(object):
|
|||
if not hasattr(context, "session"):
|
||||
# we may be called again by one of our meta-calls (like multiCall)
|
||||
# so we should only create a session if one does not already exist
|
||||
context.session = koji.auth.Session()
|
||||
context.session = auth.Session()
|
||||
try:
|
||||
context.session.validate()
|
||||
except koji.AuthLockError:
|
||||
|
|
@ -343,7 +343,7 @@ class ModXMLRPCRequestHandler(object):
|
|||
results and errors, and return those as a list."""
|
||||
results = []
|
||||
for call in calls:
|
||||
savepoint = koji.db.Savepoint('multiCall_loop')
|
||||
savepoint = db.Savepoint('multiCall_loop')
|
||||
try:
|
||||
result = self._dispatch(call['methodName'], call['params'])
|
||||
except Fault as fault:
|
||||
|
|
@ -729,13 +729,13 @@ def server_setup(environ):
|
|||
registry = get_registry(opts, plugins)
|
||||
policy = get_policy(opts, plugins)
|
||||
if opts.get('DBConnectionString'):
|
||||
koji.db.provideDBopts(dsn=opts['DBConnectionString'])
|
||||
db.provideDBopts(dsn=opts['DBConnectionString'])
|
||||
else:
|
||||
koji.db.provideDBopts(database=opts["DBName"],
|
||||
user=opts["DBUser"],
|
||||
password=opts.get("DBPass", None),
|
||||
host=opts.get("DBHost", None),
|
||||
port=opts.get("DBPort", None))
|
||||
db.provideDBopts(database=opts["DBName"],
|
||||
user=opts["DBUser"],
|
||||
password=opts.get("DBPass", None),
|
||||
host=opts.get("DBHost", None),
|
||||
port=opts.get("DBPort", None))
|
||||
except Exception:
|
||||
tb_str = ''.join(traceback.format_exception(*sys.exc_info()))
|
||||
logger.error(tb_str)
|
||||
|
|
@ -785,7 +785,7 @@ def application(environ, start_response):
|
|||
context.environ = environ
|
||||
context.policy = policy
|
||||
try:
|
||||
context.cnx = koji.db.connect()
|
||||
context.cnx = db.connect()
|
||||
except Exception:
|
||||
return offline_reply(start_response, msg="database outage")
|
||||
h = ModXMLRPCRequestHandler(registry)
|
||||
|
|
@ -844,13 +844,13 @@ def get_registry(opts, plugins):
|
|||
hostFunctions = kojihub.HostExports()
|
||||
registry.register_instance(functions)
|
||||
registry.register_module(hostFunctions, "host")
|
||||
registry.register_function(koji.auth.login)
|
||||
registry.register_function(koji.auth.sslLogin)
|
||||
registry.register_function(koji.auth.logout)
|
||||
registry.register_function(koji.auth.subsession)
|
||||
registry.register_function(koji.auth.logoutChild)
|
||||
registry.register_function(koji.auth.exclusiveSession)
|
||||
registry.register_function(koji.auth.sharedSession)
|
||||
registry.register_function(auth.login)
|
||||
registry.register_function(auth.sslLogin)
|
||||
registry.register_function(auth.logout)
|
||||
registry.register_function(auth.subsession)
|
||||
registry.register_function(auth.logoutChild)
|
||||
registry.register_function(auth.exclusiveSession)
|
||||
registry.register_function(auth.sharedSession)
|
||||
for name in opts.get('Plugins', '').split():
|
||||
plugin = plugins.get(name)
|
||||
if not plugin:
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import koji
|
|||
from koji.context import context
|
||||
from koji.plugin import callback, convert_datetime, ignore_error
|
||||
from kojihub import get_build_type
|
||||
from koji.db import QueryProcessor, InsertProcessor, DeleteProcessor
|
||||
from kojihub.db import QueryProcessor, InsertProcessor, DeleteProcessor
|
||||
|
||||
CONFIG_FILE = '/etc/koji-hub/plugins/protonmsg.conf'
|
||||
CONFIG = None
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import koji
|
||||
from koji.db import QueryProcessor, nextval
|
||||
from koji.context import context
|
||||
from koji.plugin import callback, export
|
||||
import koji.policy
|
||||
|
|
@ -21,6 +20,7 @@ from kojihub import (
|
|||
policy_get_user,
|
||||
readInheritanceData,
|
||||
)
|
||||
from kojihub.db import QueryProcessor, nextval
|
||||
|
||||
|
||||
CONFIG_FILE = "/etc/koji-hub/plugins/sidetag.conf"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import unittest
|
|||
import mock
|
||||
|
||||
import koji
|
||||
import koji.db
|
||||
import kojihub
|
||||
import kojihub.kojihub
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class TestAddHost(unittest.TestCase):
|
|||
side_effect=self.getQuery).start()
|
||||
self.queries = []
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
self.context_db.session.assertLogin = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class TestAddHostToChannel(unittest.TestCase):
|
|||
side_effect=self.getInsert).start()
|
||||
self.inserts = []
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
self.context_db.session.assertLogin = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class TestCGImporter(unittest.TestCase):
|
|||
if not os.path.exists(self.TMP_PATH):
|
||||
os.mkdir(self.TMP_PATH)
|
||||
self.path_work = mock.patch('koji.pathinfo.work').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.get_build = mock.patch('kojihub.kojihub.get_build').start()
|
||||
self.get_user = mock.patch('kojihub.kojihub.get_user').start()
|
||||
|
|
@ -271,7 +271,7 @@ class TestCGReservation(unittest.TestCase):
|
|||
self.inserts = []
|
||||
self.updates = []
|
||||
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.context_db.session.user_id = 123456
|
||||
self.mock_cursor = mock.MagicMock()
|
||||
self.context_db.cnx.cursor.return_value = self.mock_cursor
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class TestCheckVolumePolicy(unittest.TestCase):
|
|||
def tearDown(self):
|
||||
mock.patch.stopall()
|
||||
|
||||
@mock.patch('kojixmlrpc.kojihub', new=kojihub, create=True)
|
||||
@mock.patch('kojihub.kojixmlrpc.kojihub', new=kojihub, create=True)
|
||||
def load_policy(self, policy):
|
||||
'''policy is the policy dict with text values'''
|
||||
plugin = FakePlugin()
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class TestCompleteImageBuild(unittest.TestCase):
|
|||
self.pathinfo = koji.PathInfo(self.tempdir)
|
||||
mock.patch('koji.pathinfo', new=self.pathinfo).start()
|
||||
self.hostcalls = kojihub.HostExports()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
mock.patch('kojihub.kojihub.Host').start()
|
||||
self.Task = mock.patch('kojihub.kojihub.Task').start()
|
||||
self.Task.return_value.assertHost = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class TestCompleteMavenBuild(unittest.TestCase):
|
|||
mock.patch('koji.pathinfo', new=self.pathinfo).start()
|
||||
self.hostcalls = kojihub.HostExports()
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.context.opts = {'EnableMaven': True}
|
||||
mock.patch('kojihub.kojihub.Host').start()
|
||||
self.Task = mock.patch('kojihub.kojihub.Task').start()
|
||||
|
|
@ -34,8 +34,8 @@ class TestCompleteMavenBuild(unittest.TestCase):
|
|||
mock.patch.object(kojihub.BuildRoot, 'load', new=self.my_buildroot_load).start()
|
||||
mock.patch('kojihub.kojihub.import_archive_internal',
|
||||
new=self.my_import_archive_internal).start()
|
||||
mock.patch('koji.db._dml').start()
|
||||
mock.patch('koji.db._fetchSingle').start()
|
||||
mock.patch('kojihub.db._dml').start()
|
||||
mock.patch('kojihub.db._fetchSingle').start()
|
||||
mock.patch('kojihub.kojihub.build_notification').start()
|
||||
mock.patch('kojihub.kojihub.assert_policy').start()
|
||||
mock.patch('kojihub.kojihub.check_volume_policy',
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class TestCreateMavenBuild(unittest.TestCase):
|
|||
self.exports = kojihub.RootExports()
|
||||
self.session = mock.MagicMock()
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.context.session.assertPerm = mock.MagicMock()
|
||||
self.InsertProcessor = mock.patch('kojihub.kojihub.InsertProcessor',
|
||||
side_effect=self.getInsert).start()
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class TestCreateTag(unittest.TestCase):
|
|||
self.verify_name_internal = mock.patch('kojihub.kojihub.verify_name_internal').start()
|
||||
self.writeInheritanceData = mock.patch('kojihub.kojihub._writeInheritanceData').start()
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
self.context.session.assertPerm = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class TestDeleteBuild(unittest.TestCase):
|
|||
self.UpdateProcessor = mock.patch('kojihub.kojihub.UpdateProcessor',
|
||||
side_effect=self.getUpdate).start()
|
||||
self.updates = []
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.context_db.session.assertLogin = mock.MagicMock()
|
||||
self.context_db.event_id = 42
|
||||
self.context_db.session.user_id = 24
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class TestDeleteTag(unittest.TestCase):
|
|||
self.updates = []
|
||||
self.get_tag = mock.patch('kojihub.kojihub.get_tag').start()
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
self.context.session.assertPerm = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class TestEditHost(unittest.TestCase):
|
|||
side_effect=self.getUpdate).start()
|
||||
self.updates = []
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
self.context_db.session.assertLogin = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class TestEditTag(unittest.TestCase):
|
|||
self.get_perm_id = mock.patch('kojihub.kojihub.get_perm_id').start()
|
||||
self.verify_name_internal = mock.patch('kojihub.kojihub.verify_name_internal').start()
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
self.context_db.session.assertLogin = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import mock
|
|||
import unittest
|
||||
import koji
|
||||
import kojihub
|
||||
import koji.db
|
||||
|
||||
|
||||
QP = kojihub.QueryProcessor
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import kojihub
|
|||
class TestGetUserPerms(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.get_user = mock.patch('kojihub.kojihub.get_user').start()
|
||||
self.get_user_perms = mock.patch('koji.auth.get_user_perms').start()
|
||||
self.get_user_perms = mock.patch('kojihub.kojihub.get_user_perms').start()
|
||||
|
||||
def tearDown(self):
|
||||
mock.patch.stopall()
|
||||
|
|
|
|||
|
|
@ -18,9 +18,10 @@ class TestGrantPermission(unittest.TestCase):
|
|||
self.lookup_perm = mock.patch('kojihub.kojihub.lookup_perm').start()
|
||||
self.insert_processor = mock.patch('kojihub.kojihub.InsertProcessor').start()
|
||||
self.update_processor = mock.patch('kojihub.kojihub.UpdateProcessor').start()
|
||||
self.get_user_perms = mock.patch('koji.auth.get_user_perms').start()
|
||||
self.exports = kojihub.RootExports()
|
||||
self.get_user_perms = mock.patch('kojihub.kojihub.get_user_perms').start()
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.exports = kojihub.RootExports()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
self.context.session.assertPerm = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class TestGrouplist(unittest.TestCase):
|
|||
|
||||
def setUp(self):
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.get_tag = mock.patch('kojihub.kojihub.get_tag').start()
|
||||
self.lookup_tag = mock.patch('kojihub.kojihub.lookup_tag').start()
|
||||
self.lookup_group = mock.patch('kojihub.kojihub.lookup_group').start()
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class TestImportBuild(unittest.TestCase):
|
|||
|
||||
self.check_volume_policy = mock.patch('kojihub.kojihub.check_volume_policy').start()
|
||||
self.new_typed_build = mock.patch('kojihub.kojihub.new_typed_build').start()
|
||||
self._dml = mock.patch('koji.db._dml').start()
|
||||
self._dml = mock.patch('kojihub.db._dml').start()
|
||||
self.nextval = mock.patch('kojihub.kojihub.nextval').start()
|
||||
self.get_build = mock.patch('kojihub.kojihub.get_build').start()
|
||||
self.add_rpm_sig = mock.patch('kojihub.kojihub.add_rpm_sig').start()
|
||||
|
|
@ -31,7 +31,7 @@ class TestImportBuild(unittest.TestCase):
|
|||
self.import_rpm = mock.patch('kojihub.kojihub.import_rpm').start()
|
||||
self.QueryProcessor = mock.patch('kojihub.kojihub.QueryProcessor').start()
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.new_package = mock.patch('kojihub.kojihub.new_package').start()
|
||||
self.get_rpm_header = mock.patch('koji.get_rpm_header').start()
|
||||
self.pathinfo_work = mock.patch('koji.pathinfo.work').start()
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ class TestImportImageInternal(unittest.TestCase):
|
|||
def setUp(self):
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.Task = mock.patch('kojihub.kojihub.Task').start()
|
||||
self.get_build = mock.patch('kojihub.kojihub.get_build').start()
|
||||
self.get_archive_type = mock.patch('kojihub.kojihub.get_archive_type').start()
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class TestImportRPM(unittest.TestCase):
|
|||
pass
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context.session.assertPerm = mock.MagicMock()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.cursor = mock.MagicMock()
|
||||
|
||||
self.rpm_header_retval = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import unittest
|
||||
|
||||
import kojixmlrpc
|
||||
from kojihub import kojixmlrpc
|
||||
|
||||
|
||||
class TestHandler(unittest.TestCase):
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class DummyExports(object):
|
|||
class TestMulticall(unittest.TestCase):
|
||||
|
||||
def test_multicall(self):
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
kojixmlrpc.kojihub = mock.MagicMock()
|
||||
kojixmlrpc.context.opts = mock.MagicMock()
|
||||
kojixmlrpc.context.session = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class TestRemoveHostFromChannel(unittest.TestCase):
|
|||
side_effect=self.getUpdate).start()
|
||||
self.updates = []
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
self.context_db.session.assertLogin = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class TestSetHostEnabled(unittest.TestCase):
|
|||
side_effect=self.getUpdate).start()
|
||||
self.updates = []
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.get_host = mock.patch('kojihub.kojihub.get_host').start()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class TestTagBuild(unittest.TestCase):
|
|||
self.check_tag_access = mock.patch('kojihub.kojihub.check_tag_access').start()
|
||||
self.writeInheritanceData = mock.patch('kojihub.kojihub.writeInheritanceData').start()
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
self.context.session.assertPerm = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class TestGrouplist(unittest.TestCase):
|
|||
|
||||
def setUp(self):
|
||||
self.context = mock.patch('kojihub.kojihub.context').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
self.get_user = mock.patch('kojihub.kojihub.get_user').start()
|
||||
self.verify_name_internal = mock.patch('kojihub.kojihub.verify_name_internal').start()
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
|
|
|
|||
|
|
@ -5,12 +5,11 @@ import mock
|
|||
import unittest
|
||||
|
||||
import koji
|
||||
import koji.auth
|
||||
import koji.db
|
||||
import datetime
|
||||
import kojihub.auth
|
||||
|
||||
UP = koji.auth.UpdateProcessor
|
||||
QP = koji.auth.QueryProcessor
|
||||
UP = kojihub.auth.UpdateProcessor
|
||||
QP = kojihub.auth.QueryProcessor
|
||||
|
||||
|
||||
class TestAuthSession(unittest.TestCase):
|
||||
|
|
@ -29,42 +28,42 @@ class TestAuthSession(unittest.TestCase):
|
|||
return query
|
||||
|
||||
def setUp(self):
|
||||
self.context = mock.patch('kojihub.context').start()
|
||||
self.UpdateProcessor = mock.patch('koji.auth.UpdateProcessor',
|
||||
self.context = mock.patch('kojihub.auth.context').start()
|
||||
kojihub.db.context = self.context
|
||||
kojihub.auth.context = self.context
|
||||
self.UpdateProcessor = mock.patch('kojihub.auth.UpdateProcessor',
|
||||
side_effect=self.getUpdate).start()
|
||||
self.updates = []
|
||||
self.query_execute = mock.MagicMock()
|
||||
self.query_executeOne = mock.MagicMock()
|
||||
self.query_singleValue = mock.MagicMock()
|
||||
self.QueryProcessor = mock.patch('koji.auth.QueryProcessor',
|
||||
self.QueryProcessor = mock.patch('kojihub.auth.QueryProcessor',
|
||||
side_effect=self.getQuery).start()
|
||||
self.queries = []
|
||||
# It seems MagicMock will not automatically handle attributes that
|
||||
# start with "assert"
|
||||
self.context.session.assertLogin = mock.MagicMock()
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_instance(self, context):
|
||||
"""Simple auth.Session instance"""
|
||||
context.opts = {
|
||||
def test_instance(self):
|
||||
"""Simple kojihub.auth.Session instance"""
|
||||
self.context.opts = {
|
||||
'CheckClientIP': True,
|
||||
'DisableURLSessions': False,
|
||||
}
|
||||
with self.assertRaises(koji.GenericError) as cm:
|
||||
koji.auth.Session()
|
||||
kojihub.auth.Session()
|
||||
# no args in request/environment
|
||||
self.assertEqual(cm.exception.args[0], "'session-id' not specified in session args")
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def get_session_old(self, context):
|
||||
def get_session_old(self):
|
||||
"""auth.Session instance"""
|
||||
# base session from test_basic_instance
|
||||
# url-based auth - will be dropped in 1.34
|
||||
context.opts = {
|
||||
# url-based kojihub.auth - will be dropped in 1.34
|
||||
self.context.opts = {
|
||||
'CheckClientIP': True,
|
||||
'DisableURLSessions': False,
|
||||
}
|
||||
context.environ = {
|
||||
self.context.environ = {
|
||||
'QUERY_STRING': 'session-id=123&session-key=xyz&callnum=345',
|
||||
'REMOTE_ADDR': 'remote-addr',
|
||||
}
|
||||
|
|
@ -80,18 +79,17 @@ class TestAuthSession(unittest.TestCase):
|
|||
'user_id': 1},
|
||||
{'name': 'kojiadmin', 'status': 0, 'usertype': 0}]
|
||||
self.query_singleValue.return_value = 123
|
||||
s = koji.auth.Session()
|
||||
return s, context
|
||||
s = kojihub.auth.Session()
|
||||
return s, self.context
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def get_session(self, context):
|
||||
def get_session(self):
|
||||
# base session from test_basic_instance
|
||||
# header-based auth
|
||||
context.opts = {
|
||||
self.context.opts = {
|
||||
'CheckClientIP': True,
|
||||
'DisableURLSessions': True,
|
||||
}
|
||||
context.environ = {
|
||||
self.context.environ = {
|
||||
'HTTP_KOJI_SESSION_ID': '123',
|
||||
'HTTP_KOJI_SESSION_KEY': 'xyz',
|
||||
'HTTP_KOJI_CALLNUM': '345',
|
||||
|
|
@ -109,8 +107,8 @@ class TestAuthSession(unittest.TestCase):
|
|||
'user_id': 1},
|
||||
{'name': 'kojiadmin', 'status': 0, 'usertype': 0}]
|
||||
self.query_singleValue.return_value = 123
|
||||
s = koji.auth.Session()
|
||||
return s, context
|
||||
s = kojihub.auth.Session()
|
||||
return s, self.context
|
||||
|
||||
def test_session_old(self):
|
||||
self.get_session_old()
|
||||
|
|
@ -220,11 +218,10 @@ class TestAuthSession(unittest.TestCase):
|
|||
with self.assertRaises(AttributeError):
|
||||
s.non_existing_attribute
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_str(self, context):
|
||||
def test_str(self):
|
||||
"""auth.Session string representation"""
|
||||
s, cntext = self.get_session()
|
||||
context.cnx = cntext.cnx
|
||||
self.context.cnx = cntext.cnx
|
||||
|
||||
s.logged_in = False
|
||||
s.message = 'msg'
|
||||
|
|
@ -232,11 +229,10 @@ class TestAuthSession(unittest.TestCase):
|
|||
s.logged_in = True
|
||||
self.assertNotEqual(str(s), 'session: not logged in')
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_validate(self, context):
|
||||
def test_validate(self):
|
||||
"""Session.validate"""
|
||||
s, cntext = self.get_session()
|
||||
context.cnx = cntext.cnx
|
||||
self.context.cnx = cntext.cnx
|
||||
|
||||
s.lockerror = True
|
||||
with self.assertRaises(koji.AuthLockError):
|
||||
|
|
@ -245,8 +241,7 @@ class TestAuthSession(unittest.TestCase):
|
|||
s.lockerror = False
|
||||
self.assertTrue(s.validate())
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_makeShared(self, context):
|
||||
def test_makeShared(self):
|
||||
"""Session.makeShared"""
|
||||
s, _ = self.get_session()
|
||||
s.makeShared()
|
||||
|
|
@ -264,27 +259,25 @@ class TestAuthSession(unittest.TestCase):
|
|||
# all queries are tested in test_basic_instance
|
||||
|
||||
@mock.patch('socket.gethostbyname')
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_get_remote_ip(self, context, gethostbyname):
|
||||
def test_get_remote_ip(self, gethostbyname):
|
||||
"""Session.get_remote_ip"""
|
||||
s, cntext = self.get_session()
|
||||
s, _ = self.get_session()
|
||||
|
||||
context.opts = {'CheckClientIP': False}
|
||||
self.context.opts = {'CheckClientIP': False}
|
||||
self.assertEqual(s.get_remote_ip(), '-')
|
||||
|
||||
context.opts = {'CheckClientIP': True}
|
||||
self.context.opts = {'CheckClientIP': True}
|
||||
self.assertEqual(s.get_remote_ip(override='xoverride'), 'xoverride')
|
||||
|
||||
context.environ = {'REMOTE_ADDR': '123.123.123.123'}
|
||||
self.context.environ = {'REMOTE_ADDR': '123.123.123.123'}
|
||||
self.assertEqual(s.get_remote_ip(), '123.123.123.123')
|
||||
|
||||
gethostbyname.return_value = 'ip'
|
||||
context.environ = {'REMOTE_ADDR': '127.0.0.1'}
|
||||
self.context.environ = {'REMOTE_ADDR': '127.0.0.1'}
|
||||
self.assertEqual(s.get_remote_ip(), 'ip')
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_login(self, context):
|
||||
s, cntext = self.get_session()
|
||||
def test_login(self):
|
||||
s, _ = self.get_session()
|
||||
|
||||
# already logged in
|
||||
with self.assertRaises(koji.GenericError):
|
||||
|
|
@ -329,13 +322,12 @@ class TestAuthSession(unittest.TestCase):
|
|||
with self.assertRaises(koji.AuthError):
|
||||
s.login('user', 'password')
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_checkKrbPrincipal(self, context):
|
||||
def test_checkKrbPrincipal(self):
|
||||
s, cntext = self.get_session()
|
||||
self.assertIsNone(s.checkKrbPrincipal(None))
|
||||
context.opts = {'AllowedKrbRealms': '*'}
|
||||
self.context.opts = {'AllowedKrbRealms': '*'}
|
||||
self.assertIsNone(s.checkKrbPrincipal('any'))
|
||||
context.opts = {'AllowedKrbRealms': 'example.com'}
|
||||
self.context.opts = {'AllowedKrbRealms': 'example.com'}
|
||||
with self.assertRaises(koji.AuthError) as cm:
|
||||
s.checkKrbPrincipal('any')
|
||||
self.assertEqual(cm.exception.args[0],
|
||||
|
|
@ -350,8 +342,7 @@ class TestAuthSession(unittest.TestCase):
|
|||
"Kerberos principal's realm:"
|
||||
" bannedrealm is not allowed")
|
||||
self.assertIsNone(s.checkKrbPrincipal('user@example.com'))
|
||||
context.opts = {'AllowedKrbRealms': 'example.com,example.net'
|
||||
' , example.org'}
|
||||
self.context.opts = {'AllowedKrbRealms': 'example.com,example.net,example.org'}
|
||||
self.assertIsNone(s.checkKrbPrincipal('user@example.net'))
|
||||
|
||||
def test_getUserIdFromKerberos(self):
|
||||
|
|
@ -420,9 +411,8 @@ class TestAuthSession(unittest.TestCase):
|
|||
s.logout()
|
||||
self.assertEqual(cm.exception.args[0], 'Not logged in')
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_logout_logged(self, context):
|
||||
s, cntext = self.get_session()
|
||||
def test_logout_logged(self):
|
||||
s, _ = self.get_session()
|
||||
s.logged_in = True
|
||||
s.logout()
|
||||
|
||||
|
|
@ -446,9 +436,8 @@ class TestAuthSession(unittest.TestCase):
|
|||
s.logoutChild(111)
|
||||
self.assertEqual(cm.exception.args[0], 'Not logged in')
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_logoutChild_logged(self, context):
|
||||
s, cntext = self.get_session()
|
||||
def test_logoutChild_logged(self):
|
||||
s, _ = self.get_session()
|
||||
s.logged_in = True
|
||||
s.logoutChild(111)
|
||||
|
||||
|
|
@ -493,9 +482,8 @@ class TestAuthSession(unittest.TestCase):
|
|||
self.assertEqual(len(self.queries), 5)
|
||||
self.assertEqual(len(self.updates), 2)
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_makeExclusive(self, context):
|
||||
s, cntext = self.get_session()
|
||||
def test_makeExclusive(self):
|
||||
s, _ = self.get_session()
|
||||
s.master = None
|
||||
s.exclusive = False
|
||||
self.query_singleValue.return_value = 123
|
||||
|
|
@ -635,13 +623,13 @@ class TestAuthSession(unittest.TestCase):
|
|||
# functions outside Session object
|
||||
|
||||
def test_get_user_data(self):
|
||||
"""koji.auth.get_user_data"""
|
||||
"""auth.get_user_data"""
|
||||
self.query_executeOne.return_value = None
|
||||
self.assertEqual(len(self.queries), 0)
|
||||
|
||||
self.query_executeOne.return_value = {'name': 'name', 'status': 'status',
|
||||
'usertype': 'usertype'}
|
||||
koji.auth.get_user_data(1)
|
||||
kojihub.auth.get_user_data(1)
|
||||
self.assertEqual(len(self.queries), 1)
|
||||
query = self.queries[0]
|
||||
self.assertEqual(query.tables, ['users'])
|
||||
|
|
@ -650,8 +638,8 @@ class TestAuthSession(unittest.TestCase):
|
|||
self.assertEqual(query.columns, ['name', 'status', 'usertype'])
|
||||
|
||||
def test_get_user_groups(self):
|
||||
"""koji.auth.get_user_groups"""
|
||||
koji.auth.get_user_groups(1)
|
||||
"""auth.get_user_groups"""
|
||||
kojihub.auth.get_user_groups(1)
|
||||
self.assertEqual(len(self.queries), 1)
|
||||
query = self.queries[0]
|
||||
self.assertEqual(query.tables, ['user_groups'])
|
||||
|
|
@ -661,8 +649,8 @@ class TestAuthSession(unittest.TestCase):
|
|||
self.assertEqual(query.columns, ['group_id', 'name'])
|
||||
|
||||
def test_get_user_perms(self):
|
||||
"""koji.auth.get_user_perms"""
|
||||
koji.auth.get_user_perms(1)
|
||||
"""auth.get_user_perms"""
|
||||
kojihub.auth.get_user_perms(1)
|
||||
self.assertEqual(len(self.queries), 1)
|
||||
query = self.queries[0]
|
||||
self.assertEqual(query.tables, ['user_perms'])
|
||||
|
|
@ -670,14 +658,13 @@ class TestAuthSession(unittest.TestCase):
|
|||
self.assertEqual(query.clauses, ['active = TRUE', 'user_id=%(user_id)s'])
|
||||
self.assertEqual(query.columns, ['name'])
|
||||
|
||||
@mock.patch('koji.auth.context')
|
||||
def test_logout_logged_not_owner(self, context):
|
||||
s, cntext = self.get_session()
|
||||
def test_logout_logged_not_owner(self):
|
||||
s, _ = self.get_session()
|
||||
|
||||
s.logged_in = True
|
||||
# session_id without admin perms and not owner
|
||||
context.session.hasPerm.return_value = False
|
||||
context.session.user_id.return_value = 123
|
||||
self.context.session.hasPerm.return_value = False
|
||||
self.context.session.user_id.return_value = 123
|
||||
self.query_singleValue.return_value = None
|
||||
with self.assertRaises(koji.ActionNotAllowed) as ex:
|
||||
s.logout(session_id=1)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import kojihub
|
|||
|
||||
class TestInsertProcessor(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
|
||||
def tearDown(self):
|
||||
mock.patch.stopall()
|
||||
|
|
@ -92,7 +92,7 @@ class TestInsertProcessor(unittest.TestCase):
|
|||
|
||||
class TestBulkInsertProcessor(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
|
||||
def tearDown(self):
|
||||
mock.patch.stopall()
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class TestQueryProcessor(unittest.TestCase):
|
|||
)
|
||||
self.original_chunksize = kojihub.QueryProcessor.iterchunksize
|
||||
kojihub.QueryProcessor.iterchunksize = 2
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
|
||||
def tearDown(self):
|
||||
kojihub.QueryProcessor.iterchunksize = self.original_chunksize
|
||||
|
|
@ -125,7 +125,7 @@ class TestQueryProcessor(unittest.TestCase):
|
|||
result = next(generator)
|
||||
self.assertEqual(result, {'something': 'value number 3'})
|
||||
|
||||
@mock.patch('koji.db._multiRow')
|
||||
@mock.patch('kojihub.db._multiRow')
|
||||
def test_execution_as_list_transform(self, multirow):
|
||||
multirow.return_value = [{'col1': 'result_1_col_1', 'col2': 'result_1_col_2'},
|
||||
{'col1': 'result_2_col_1', 'col2': 'result_2_col_2'}]
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import kojihub
|
|||
class TestSavepoint(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.dml = mock.patch('koji.db._dml').start()
|
||||
self.context_db = mock.patch('koji.db.context').start()
|
||||
self.dml = mock.patch('kojihub.db._dml').start()
|
||||
self.context_db = mock.patch('kojihub.db.context').start()
|
||||
|
||||
def tearDown(self):
|
||||
mock.patch.stopall()
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class TestUpdateProcessor(unittest.TestCase):
|
|||
expected = {'data.foo': 'bar'}
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@mock.patch('koji.db.context')
|
||||
@mock.patch('kojihub.db.context')
|
||||
def test_simple_execution_with_iterate(self, context_db):
|
||||
cursor = mock.MagicMock()
|
||||
context_db.cnx.cursor.return_value = cursor
|
||||
|
|
|
|||
2
tox.ini
2
tox.ini
|
|
@ -34,7 +34,7 @@ commands_pre =
|
|||
[testenv:py3]
|
||||
setenv =
|
||||
{[testenv]setenv}
|
||||
PYTHONPATH=kojihub/.:plugins/hub/.:plugins/builder/.:plugins/cli/.:cli/.:www/lib
|
||||
PYTHONPATH=.:plugins/hub/.:plugins/builder/.:plugins/cli/.:cli/.:www/lib
|
||||
commands_pre =
|
||||
{[testenv]commands_pre}
|
||||
{envbindir}/coverage3 erase --rcfile .coveragerc3
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ from optparse import OptionParser
|
|||
from koji.context import context
|
||||
|
||||
import koji
|
||||
import koji.db
|
||||
from koji.db import DeleteProcessor, QueryProcessor, BulkInsertProcessor
|
||||
import kojihub.db
|
||||
from kojihub.db import DeleteProcessor, QueryProcessor, BulkInsertProcessor
|
||||
|
||||
|
||||
def clean_sessions(cursor, vacuum, test, age, absolute):
|
||||
|
|
@ -223,17 +223,17 @@ if __name__ == "__main__":
|
|||
opts[name] = default
|
||||
|
||||
if opts.get('DBConnectionString'):
|
||||
koji.db.provideDBopts(dsn=opts['DBConnectionString'])
|
||||
kojihub.db.provideDBopts(dsn=opts['DBConnectionString'])
|
||||
else:
|
||||
if opts['DBHost'] is None:
|
||||
opts['DBHost'] = opts['DBhost']
|
||||
koji.db.provideDBopts(database=opts["DBName"],
|
||||
user=opts["DBUser"],
|
||||
password=opts.get("DBPass", None),
|
||||
host=opts.get("DBHost", None),
|
||||
port=opts.get("DBPort", None))
|
||||
kojihub.db.provideDBopts(database=opts["DBName"],
|
||||
user=opts["DBUser"],
|
||||
password=opts.get("DBPass", None),
|
||||
host=opts.get("DBHost", None),
|
||||
port=opts.get("DBPort", None))
|
||||
|
||||
context.cnx = koji.db.connect()
|
||||
context.cnx = kojihub.db.connect()
|
||||
context.cnx.set_session(autocommit=True)
|
||||
cursor = context.cnx.cursor()
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue