diff --git a/src/wuttjamaican/app.py b/src/wuttjamaican/app.py index 6a5f288..8e4d891 100644 --- a/src/wuttjamaican/app.py +++ b/src/wuttjamaican/app.py @@ -30,7 +30,7 @@ import sys import warnings from wuttjamaican.util import (load_entry_points, load_object, - make_title, make_uuid, make_true_uuid, + make_title, make_uuid, progress_loop, resource_path) @@ -491,44 +491,14 @@ class AppHandler: """ return make_title(text) - def make_true_uuid(self): - """ - Generate a new v7 UUID value. - - By default this simply calls - :func:`wuttjamaican.util.make_true_uuid()`. - - :returns: :class:`python:uuid.UUID` instance - - .. warning:: - - For now, callers should use this method when they want a - proper UUID instance, whereas :meth:`make_uuid()` will - always return a string. - - However once all dependent logic has been refactored to - support proper UUID data type, then ``make_uuid()`` will - return those and this method will eventually be removed. - """ - return make_true_uuid() - def make_uuid(self): """ - Generate a new v7 UUID value. + Generate a new UUID value. By default this simply calls :func:`wuttjamaican.util.make_uuid()`. :returns: UUID value as 32-character string. - - .. warning:: - - For now, this method always returns a string. - - However once all dependent logic has been refactored to - support proper UUID data type, then this method will return - those and the :meth:`make_true_uuid()` method will - eventually be removed. """ return make_uuid() diff --git a/src/wuttjamaican/cli/make_uuid.py b/src/wuttjamaican/cli/make_uuid.py index 84f4182..21b9cf7 100644 --- a/src/wuttjamaican/cli/make_uuid.py +++ b/src/wuttjamaican/cli/make_uuid.py @@ -40,5 +40,5 @@ def make_uuid( """ config = ctx.parent.wutta_config app = config.get_app() - uuid = app.make_true_uuid() + uuid = app.make_uuid() sys.stdout.write(f"{uuid}\n") diff --git a/src/wuttjamaican/db/alembic/script.py.mako b/src/wuttjamaican/db/alembic/script.py.mako index b49109f..fbc4b07 100644 --- a/src/wuttjamaican/db/alembic/script.py.mako +++ b/src/wuttjamaican/db/alembic/script.py.mako @@ -9,7 +9,6 @@ from typing import Sequence, Union from alembic import op import sqlalchemy as sa -import wuttjamaican.db.util ${imports if imports else ""} # revision identifiers, used by Alembic. diff --git a/src/wuttjamaican/db/alembic/versions/6be0ed225f4d_convert_uuid_types.py b/src/wuttjamaican/db/alembic/versions/6be0ed225f4d_convert_uuid_types.py deleted file mode 100644 index 26db2ce..0000000 --- a/src/wuttjamaican/db/alembic/versions/6be0ed225f4d_convert_uuid_types.py +++ /dev/null @@ -1,66 +0,0 @@ -"""convert uuid types - -Revision ID: 6be0ed225f4d -Revises: 6bf900765500 -Create Date: 2024-11-30 17:03:08.930050 - -""" -import uuid as _uuid -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -import wuttjamaican.db.util - - -# revision identifiers, used by Alembic. -revision: str = '6be0ed225f4d' -down_revision: Union[str, None] = '6bf900765500' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - - # upgrade (convert uuid) - op.add_column('upgrade', sa.Column('uuid_true', wuttjamaican.db.util.UUID(), nullable=True)) - upgrade = sa.sql.table('upgrade', - sa.sql.column('uuid'), - sa.sql.column('uuid_true')) - - engine = op.get_bind() - cursor = engine.execute(upgrade.select()) - for row in cursor.fetchall(): - if row['uuid']: - uuid_true = _uuid.UUID(row['uuid']) - engine.execute(upgrade.update()\ - .where(upgrade.c.uuid == row['uuid'])\ - .values({'uuid_true': uuid_true})) - - op.drop_constraint('pk_upgrade', 'upgrade', type_='primary') - op.drop_column('upgrade', 'uuid') - op.alter_column('upgrade', 'uuid_true', new_column_name='uuid') - op.create_primary_key('pk_upgrade', 'upgrade', ['uuid']) - - -def downgrade() -> None: - - # upgrade (convert uuid) - op.add_column('upgrade', sa.Column('uuid_str', sa.VARCHAR(length=32), nullable=True)) - upgrade = sa.sql.table('upgrade', - sa.sql.column('uuid'), - sa.sql.column('uuid_str')) - - engine = op.get_bind() - cursor = engine.execute(upgrade.select()) - for row in cursor.fetchall(): - if row['uuid']: - uuid_str = row['uuid'].hex - engine.execute(upgrade.update()\ - .where(upgrade.c.uuid == row['uuid'])\ - .values({'uuid_str': uuid_str})) - - op.drop_constraint('pk_upgrade', 'upgrade', type_='primary') - op.drop_column('upgrade', 'uuid') - op.alter_column('upgrade', 'uuid_str', new_column_name='uuid') - op.create_primary_key('pk_upgrade', 'upgrade', ['uuid']) diff --git a/src/wuttjamaican/db/model/upgrades.py b/src/wuttjamaican/db/model/upgrades.py index 6893ba6..c8f3666 100644 --- a/src/wuttjamaican/db/model/upgrades.py +++ b/src/wuttjamaican/db/model/upgrades.py @@ -31,8 +31,6 @@ from sqlalchemy import orm from .base import Base, uuid_column, uuid_fk_column from wuttjamaican.enum import UpgradeStatus -from wuttjamaican.db.util import UUID -from wuttjamaican.util import make_true_uuid class Upgrade(Base): @@ -41,7 +39,7 @@ class Upgrade(Base): """ __tablename__ = 'upgrade' - uuid = uuid_column(UUID(), default=make_true_uuid) + uuid = uuid_column() created = sa.Column(sa.DateTime(timezone=True), nullable=False, default=datetime.datetime.now, doc=""" diff --git a/src/wuttjamaican/db/util.py b/src/wuttjamaican/db/util.py index 0df0e68..88362c4 100644 --- a/src/wuttjamaican/db/util.py +++ b/src/wuttjamaican/db/util.py @@ -24,10 +24,7 @@ Database Utilities """ -import uuid as _uuid - import sqlalchemy as sa -from sqlalchemy.dialects.postgresql import UUID as PGUUID from wuttjamaican.util import make_uuid @@ -60,61 +57,14 @@ class ModelBase: return getattr(self, key) -class UUID(sa.types.TypeDecorator): - """ - Platform-independent UUID type. - - Uses PostgreSQL's UUID type, otherwise uses CHAR(32), storing as - stringified hex values. - - This type definition is based on example from the `SQLAlchemy - documentation - `_. - """ - impl = sa.CHAR - cache_ok = True - """ """ # nb. suppress sphinx autodoc for cache_ok - - def load_dialect_impl(self, dialect): - """ """ - if dialect.name == "postgresql": - return dialect.type_descriptor(PGUUID()) - else: - return dialect.type_descriptor(sa.CHAR(32)) - - def process_bind_param(self, value, dialect): - """ """ - if value is None: - return value - elif dialect.name == "postgresql": - return str(value) - else: - if not isinstance(value, _uuid.UUID): - return "%.32x" % _uuid.UUID(value).int - else: - # hexstring - return "%.32x" % value.int - - def process_result_value(self, value, dialect): - """ """ - if value is None: - return value - else: - if not isinstance(value, _uuid.UUID): - value = _uuid.UUID(value) - return value - - def uuid_column(*args, **kwargs): """ Returns a UUID column for use as a table's primary key. """ - if not args: - args = (sa.String(length=32),) kwargs.setdefault('primary_key', True) kwargs.setdefault('nullable', False) kwargs.setdefault('default', make_uuid) - return sa.Column(*args, **kwargs) + return sa.Column(sa.String(length=32), *args, **kwargs) def uuid_fk_column(target_column, *args, **kwargs): @@ -124,6 +74,4 @@ def uuid_fk_column(target_column, *args, **kwargs): :param target_column: Name of the table column on the remote side, e.g. ``'user.uuid'``. """ - if not args: - args = (sa.String(length=32), sa.ForeignKey(target_column)) - return sa.Column(*args, **kwargs) + return sa.Column(sa.String(length=32), sa.ForeignKey(target_column), *args, **kwargs) diff --git a/src/wuttjamaican/install.py b/src/wuttjamaican/install.py index cf9e3ac..2212fa2 100644 --- a/src/wuttjamaican/install.py +++ b/src/wuttjamaican/install.py @@ -124,7 +124,7 @@ class InstallHandler(GenericHandler): try: paths.insert(0, self.app.resource_path(f'{self.pkg_name}:templates/install')) - except (TypeError, ModuleNotFoundError): + except ModuleNotFoundError: pass self.templates = TemplateLookup(directories=paths) diff --git a/src/wuttjamaican/util.py b/src/wuttjamaican/util.py index 5574020..48c321d 100644 --- a/src/wuttjamaican/util.py +++ b/src/wuttjamaican/util.py @@ -171,43 +171,13 @@ def make_title(text): return ' '.join([x.capitalize() for x in words]) -def make_true_uuid(): - """ - Generate a new v7 UUID value. - - :returns: :class:`python:uuid.UUID` instance - - .. warning:: - - For now, callers should use this function when they want a - proper UUID instance, whereas :func:`make_uuid()` will always - return a string. - - However once all dependent logic has been refactored to support - proper UUID data type, then ``make_uuid()`` will return those - and this function will eventually be removed. - """ - return uuid7() - - -# TODO: deprecate this logic, and reclaim this name -# but using the above logic def make_uuid(): """ - Generate a new v7 UUID value. + Generate a universally-unique identifier. :returns: A 32-character hex string. - - .. warning:: - - For now, this function always returns a string. - - However once all dependent logic has been refactored to support - proper UUID data type, then this function will return those and - the :func:`make_true_uuid()` function will eventually be - removed. """ - return make_true_uuid().hex + return uuid7().hex def parse_bool(value): diff --git a/tests/db/test_util.py b/tests/db/test_util.py index 2f0c9ed..566b68a 100644 --- a/tests/db/test_util.py +++ b/tests/db/test_util.py @@ -1,16 +1,12 @@ # -*- coding: utf-8; -*- -import uuid as _uuid from unittest import TestCase -from unittest.mock import MagicMock try: import sqlalchemy as sa - from sqlalchemy.dialects.postgresql import UUID as PGUUID from wuttjamaican.db import util as mod from wuttjamaican.db.model.base import Setting - from wuttjamaican.util import make_true_uuid except ImportError: pass else: @@ -26,88 +22,6 @@ else: self.assertEqual(setting['name'], 'foo') - class TestUUID(TestCase): - - def test_load_dialect_impl(self): - typ = mod.UUID() - dialect = MagicMock() - - # TODO: this doesn't really test anything, but gives us - # coverage at least.. - - # postgres - dialect.name = 'postgresql' - dialect.type_descriptor.return_value = 42 - result = typ.load_dialect_impl(dialect) - self.assertTrue(dialect.type_descriptor.called) - self.assertEqual(result, 42) - - # other - dialect.name = 'mysql' - dialect.type_descriptor.return_value = 43 - dialect.type_descriptor.reset_mock() - result = typ.load_dialect_impl(dialect) - self.assertTrue(dialect.type_descriptor.called) - self.assertEqual(result, 43) - - def test_process_bind_param_postgres(self): - typ = mod.UUID() - dialect = MagicMock() - dialect.name = 'postgresql' - - # null - result = typ.process_bind_param(None, dialect) - self.assertIsNone(result) - - # string - uuid_str = make_true_uuid().hex - result = typ.process_bind_param(uuid_str, dialect) - self.assertEqual(result, uuid_str) - - # uuid - uuid_true = make_true_uuid() - result = typ.process_bind_param(uuid_true, dialect) - self.assertEqual(result, str(uuid_true)) - - def test_process_bind_param_other(self): - typ = mod.UUID() - dialect = MagicMock() - dialect.name = 'mysql' - - # null - result = typ.process_bind_param(None, dialect) - self.assertIsNone(result) - - # string - uuid_str = make_true_uuid().hex - result = typ.process_bind_param(uuid_str, dialect) - self.assertEqual(result, uuid_str) - - # uuid - uuid_true = make_true_uuid() - result = typ.process_bind_param(uuid_true, dialect) - self.assertEqual(result, uuid_true.hex) - - def test_process_result_value(self): - typ = mod.UUID() - dialect = MagicMock() - - # null - result = typ.process_result_value(None, dialect) - self.assertIsNone(result) - - # string - uuid_str = make_true_uuid().hex - result = typ.process_result_value(uuid_str, dialect) - self.assertIsInstance(result, _uuid.UUID) - self.assertEqual(result.hex, uuid_str) - - # uuid - uuid_true = make_true_uuid() - result = typ.process_result_value(uuid_true, dialect) - self.assertIs(result, uuid_true) - - class TestUUIDColumn(TestCase): def test_basic(self): diff --git a/tests/test_app.py b/tests/test_app.py index 4504ba9..31d266d 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -5,7 +5,6 @@ import shutil import sys import tempfile import warnings -import uuid as _uuid from unittest import TestCase from unittest.mock import patch, MagicMock @@ -386,10 +385,6 @@ app_title = WuttaTest uuid = self.app.make_uuid() self.assertEqual(len(uuid), 32) - def test_make_true_uuid(self): - uuid = self.app.make_true_uuid() - self.assertIsInstance(uuid, _uuid.UUID) - def test_progress_loop(self): def act(obj, i): diff --git a/tests/test_install.py b/tests/test_install.py index 78ae370..d4c7844 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -74,15 +74,23 @@ class TestInstallHandler(ConfigTestCase): 'dburl': f'sqlite:///{self.tempdir}/poser.sqlite', } - with patch.object(handler, 'get_dbinfo', return_value=dbinfo): - with patch.object(handler, 'make_appdir') as make_appdir: + orig_import = __import__ + mock_prompt = MagicMock() + + def mock_import(name, globals=None, locals=None, fromlist=(), level=0): + if name == 'prompt_toolkit': + if fromlist == ('prompt',): + return MagicMock(prompt=mock_prompt) + return orig_import(name, globals, locals, fromlist, level) + + with patch('builtins.__import__', side_effect=mock_import): + with patch.object(handler, 'get_dbinfo', return_value=dbinfo): with patch.object(handler, 'install_db_schema') as install_db_schema: # nb. just for sanity/coverage install_db_schema.return_value = True self.assertFalse(hasattr(handler, 'schema_installed')) handler.do_install_steps() - self.assertTrue(make_appdir.called) self.assertTrue(handler.schema_installed) install_db_schema.assert_called_once_with(dbinfo['dburl'])