Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-Ming for openSUSE:Factory checked in at 2022-03-24 22:58:28 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-Ming (Old) and /work/SRC/openSUSE:Factory/.python-Ming.new.1900 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-Ming" Thu Mar 24 22:58:28 2022 rev:14 rq:964656 version:0.11.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-Ming/python-Ming.changes 2021-06-01 10:36:19.964697718 +0200 +++ /work/SRC/openSUSE:Factory/.python-Ming.new.1900/python-Ming.changes 2022-03-24 23:00:40.128405090 +0100 @@ -1,0 +2,24 @@ +Thu Mar 24 09:49:59 UTC 2022 - [email protected] + +- version update to 0.11.2 + 0.11.2 (Oct 15, 2021) + --------------------- + * MIM: support distinct() usage on fields that are lists + * improve a few type hints + 0.11.1 (Sep 9, 2021) + --------------------- + * Include py.typed and .pyi files in distribution + 0.11.0 (Sep 9, 2021) + --------------------- + * Drop support for Python 2.7, 3.3, and 3.4 + * Support for Python 3.9 + * MIM: support sparse unique indexes + * propagate return values from various update/delete/insert operations + * Support __init_subclass__ arguments + * validate() may not have been validating sub-documents + * Add some type annotations +- added patches + fix https://github.com/TurboGears/Ming/issues/39 + + python-Ming-no-mock.patch + +------------------------------------------------------------------- Old: ---- Ming-0.10.3.tar.gz New: ---- Ming-0.11.2.tar.gz python-Ming-no-mock.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-Ming.spec ++++++ --- /var/tmp/diff_new_pack.rPuQuu/_old 2022-03-24 23:00:40.568405514 +0100 +++ /var/tmp/diff_new_pack.rPuQuu/_new 2022-03-24 23:00:40.584405530 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-Ming # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-Ming -Version: 0.10.3 +Version: 0.11.2 Release: 0 Summary: Database mapping layer for MongoDB on Python License: MIT @@ -26,6 +26,8 @@ URL: https://github.com/TurboGears/Ming Source: https://files.pythonhosted.org/packages/source/M/Ming/Ming-%{version}.tar.gz Source1: https://raw.githubusercontent.com/TurboGears/Ming/master/LICENSE.txt +# https://github.com/TurboGears/Ming/issues/39 +Patch0: python-Ming-no-mock.patch BuildRequires: %{python_module FormEncode >= 1.2.1} BuildRequires: %{python_module WebOb} BuildRequires: %{python_module WebTest} @@ -48,6 +50,7 @@ %prep %setup -q -n Ming-%{version} +%patch0 -p1 cp %{SOURCE1} . # gridfs fails ++++++ Ming-0.10.3.tar.gz -> Ming-0.11.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/LICENSE.txt new/Ming-0.11.2/LICENSE.txt --- old/Ming-0.10.3/LICENSE.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/Ming-0.11.2/LICENSE.txt 2020-06-17 23:36:54.000000000 +0200 @@ -0,0 +1,10 @@ +This is the MIT license: +http://www.opensource.org/licenses/mit-license.php + +Copyright (c) 2019 Rick Copeland, Alessandro Molina and contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/MANIFEST.in new/Ming-0.11.2/MANIFEST.in --- old/Ming-0.10.3/MANIFEST.in 1970-01-01 01:00:00.000000000 +0100 +++ new/Ming-0.11.2/MANIFEST.in 2021-09-10 00:28:43.000000000 +0200 @@ -0,0 +1,6 @@ +## This is a MANIFEST template file. Listed here are commands and patterns +## matching files that would not get automatically included in a package. +## https://packaging.python.org/guides/using-manifest-in/ + +recursive-include ming *.pyi +include ming/py.typed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/Ming.egg-info/PKG-INFO new/Ming-0.11.2/Ming.egg-info/PKG-INFO --- old/Ming-0.10.3/Ming.egg-info/PKG-INFO 2021-03-10 15:59:42.000000000 +0100 +++ new/Ming-0.11.2/Ming.egg-info/PKG-INFO 2021-10-15 17:20:04.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: Ming -Version: 0.10.3 +Version: 0.11.2 Summary: Bringing order to Mongo since 2009 Home-page: https://github.com/TurboGears/Ming Author: Rick Copeland @@ -17,12 +17,11 @@ Classifier: Programming Language :: Python Classifier: Topic :: Database Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Requires-Python: >=3.5 Provides-Extra: configure diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/Ming.egg-info/SOURCES.txt new/Ming-0.11.2/Ming.egg-info/SOURCES.txt --- old/Ming-0.10.3/Ming.egg-info/SOURCES.txt 2021-03-10 15:59:42.000000000 +0100 +++ new/Ming-0.11.2/Ming.egg-info/SOURCES.txt 2021-10-15 17:20:04.000000000 +0200 @@ -1,3 +1,5 @@ +LICENSE.txt +MANIFEST.in README.rst setup.cfg setup.py @@ -10,30 +12,34 @@ Ming.egg-info/zip-safe ming/__init__.py ming/base.py +ming/base.pyi ming/compat.py ming/config.py ming/datastore.py ming/declarative.py +ming/declarative.pyi ming/exc.py ming/fs.py ming/metadata.py +ming/metadata.pyi ming/mim.py +ming/py.typed ming/schema.py ming/session.py ming/utils.py ming/version.py -ming/distutils_commands/__init__.py -ming/distutils_commands/git_tag.py -ming/distutils_commands/sf_upload.py ming/odm/__init__.py ming/odm/base.py ming/odm/declarative.py +ming/odm/declarative.pyi ming/odm/icollection.py ming/odm/identity_map.py ming/odm/mapper.py +ming/odm/mapper.pyi ming/odm/middleware.py ming/odm/odmsession.py ming/odm/property.py +ming/odm/property.pyi ming/odm/unit_of_work.py ming/orm/__init__.py ming/orm/base.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/PKG-INFO new/Ming-0.11.2/PKG-INFO --- old/Ming-0.10.3/PKG-INFO 2021-03-10 15:59:42.000000000 +0100 +++ new/Ming-0.11.2/PKG-INFO 2021-10-15 17:20:06.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: Ming -Version: 0.10.3 +Version: 0.11.2 Summary: Bringing order to Mongo since 2009 Home-page: https://github.com/TurboGears/Ming Author: Rick Copeland @@ -17,12 +17,11 @@ Classifier: Programming Language :: Python Classifier: Topic :: Database Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Requires-Python: >=3.5 Provides-Extra: configure diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/README.rst new/Ming-0.11.2/README.rst --- old/Ming-0.10.3/README.rst 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/README.rst 2021-09-09 21:37:10.000000000 +0200 @@ -1,11 +1,11 @@ Ming ==== -.. image:: https://travis-ci.org/TurboGears/Ming.png - :target: https://travis-ci.org/TurboGears/Ming +.. image:: https://github.com/TurboGears/Ming/actions/workflows/main.yml/badge.svg + :target: https://github.com/TurboGears/Ming/actions/workflows/main.yml -.. image:: https://coveralls.io/repos/TurboGears/Ming/badge.png - :target: https://coveralls.io/r/TurboGears/Ming +.. image:: https://codecov.io/gh/TurboGears/Ming/branch/master/graph/badge.svg?token=Iy3CU62Ga5 + :target: https://codecov.io/gh/TurboGears/Ming .. image:: https://img.shields.io/pypi/v/Ming.svg :target: https://pypi.python.org/pypi/Ming diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/base.pyi new/Ming-0.11.2/ming/base.pyi --- old/Ming-0.10.3/ming/base.pyi 1970-01-01 01:00:00.000000000 +0100 +++ new/Ming-0.11.2/ming/base.pyi 2021-09-09 22:21:13.000000000 +0200 @@ -0,0 +1,27 @@ +from typing import TypeVar, Any, Iterator, List, Optional, overload, Literal, Tuple + +import pymongo + + +def __getattr__(name) -> Any: ... # marks file as incomplete + +M = TypeVar('M') + +class Cursor(Iterator[M]): + + def count(self) -> int: ... + def distinct(self, key: str, *args, **kwargs) -> list: ... + def limit(self, limit: int) -> Cursor[M]: ... + def skip(self, skip: int) -> Cursor[M]: ... + def hint(self, index) -> Cursor[M]: ... + + @overload + def sort(self, list: List[Tuple[str, int]]) -> Cursor[M]: ... + @overload + def sort(self, list: List[list]) -> Cursor[M]: ... + @overload + def sort(self, key: str, direction: int = pymongo.ASCENDING) -> Cursor[M]: ... + + def one(self) -> M: ... + def first(self) -> Optional[M]: ... + def all(self) -> List[M]: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/datastore.py new/Ming-0.11.2/ming/datastore.py --- old/Ming-0.10.3/ming/datastore.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/datastore.py 2021-09-09 23:37:56.000000000 +0200 @@ -5,6 +5,7 @@ import six from six.moves import urllib from threading import Lock +from typing import Union from pymongo import MongoClient from pymongo.errors import ConnectionFailure, InvalidURI @@ -13,6 +14,8 @@ from . import mim from . import exc +Conn = Union[mim.Connection, MongoClient] + def create_engine(*args, **kwargs): """Creates a new :class:`.Engine` instance. @@ -120,7 +123,7 @@ return self.conn[name] @property - def conn(self): + def conn(self) -> Conn: """This is the pymongo connection itself.""" if self._conn is None: self.connect() return self._conn diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/declarative.py new/Ming-0.11.2/ming/declarative.py --- old/Ming-0.10.3/ming/declarative.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/declarative.py 2021-09-09 21:37:10.000000000 +0200 @@ -5,7 +5,7 @@ class _DocumentMeta(type): - def __new__(meta, classname, bases, dct): + def __new__(meta, classname, bases, dct, **kwargs): mm = _build_mongometa(bases, dct) collection_name = mm.name session = mm.session @@ -42,7 +42,7 @@ migrate = getattr(migrate, '__func__', migrate) if before_save: before_save = getattr(before_save, '__func__', before_save) - cls = type.__new__(meta, classname, bases, clsdct) + cls = type.__new__(meta, classname, bases, clsdct, **kwargs) m = _ClassManager( cls, collection_name, session, fields, indexes, polymorphic_on=polymorphic_on, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/declarative.pyi new/Ming-0.11.2/ming/declarative.pyi --- old/Ming-0.10.3/ming/declarative.pyi 1970-01-01 01:00:00.000000000 +0100 +++ new/Ming-0.11.2/ming/declarative.pyi 2021-09-09 22:21:13.000000000 +0200 @@ -0,0 +1,57 @@ +from typing import TypeVar, Mapping, Any + +from ming.base import Object +from ming.metadata import Manager + +M = TypeVar('M') + + +# should actually be the following, but pycharm doesn't like to recognize underscore-prefixed names in .pyi files? +# from ming.metadata import _Document +# class _Document(Object): ...methods... +# class Document(_Document): +class Document(Object): + def __init__(self, data:Mapping=None, skip_from_bson=False) -> None: ... + + @classmethod + def make(cls, data, allow_extra=False, strip_extra=True) -> Document: ... + + # ... + # class __mongometa__: + # name: Any = ... + # session: Any = ... + # indexes: Any = ... + + m: Manager[Document] + + """ + The 'm' type above is not specific enough. How can an owning class know what type its .m. deals with? Other + ORMs use mypy plugins: + + django-stubs: + has BaseManager[Any] not parametrized to the owning model type + https://github.com/typeddjango/django-stubs/blob/master/django-stubs/db/models/base.pyi#L25 + has a mypy plugin to add a lot of contextual information + + sqlalchemy-stubs: + doesn't use quite so magical of a descriptor property, instead has: + session.query(MyClass).filter(...) + typed with: + def query(self, entity: Union[Type[_T], Column[_T]], **kwargs) -> Query[_T]: ... + has a mypy plugin too + + https://mypy.readthedocs.io/en/stable/more_types.html#precise-typing-of-alternative-constructors + implies we could do this, but doesn't work for pycharm: + BT = TypeVar('BT', bound='Document') + m: Manager[BT] + + Our approach: in each model class, specify what its manager is, like this. Its in quotes since Manager isn't a real class and has to be conditionally imported + if typing.TYPE_CHECKING: + from ming.metadata import Manager + + class WikiPage(...): + ... + m: 'Manager[WikiPage]' + """ + +def __getattr__(name) -> Any: ... # marks file as incomplete \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/distutils_commands/__init__.py new/Ming-0.11.2/ming/distutils_commands/__init__.py --- old/Ming-0.10.3/ming/distutils_commands/__init__.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/distutils_commands/__init__.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,3 +0,0 @@ -from ming.distutils_commands.git_tag import git_tag - -__all__ = ['git_tag'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/distutils_commands/git_tag.py new/Ming-0.11.2/ming/distutils_commands/git_tag.py --- old/Ming-0.10.3/ming/distutils_commands/git_tag.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/distutils_commands/git_tag.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,19 +0,0 @@ -import os - -from setuptools import Command - -class git_tag(Command): - description = "tag a release" - user_options = [] - - def initialize_options(self): - pass - - def finalize_options(self): - pass - - def run(self): - tagname = self.distribution.get_version() - cmd = 'git tag %s' % tagname - os.system(cmd) - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/distutils_commands/sf_upload.py new/Ming-0.11.2/ming/distutils_commands/sf_upload.py --- old/Ming-0.10.3/ming/distutils_commands/sf_upload.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/distutils_commands/sf_upload.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,71 +0,0 @@ -import os -import string -from getpass import getpass - -from setuptools import Command - -class sf_upload(Command): - description = "Upload a release to SourceForge" - user_options = [ - ('sf-user=', 'u', - 'SourceForge username'), - ('sf-project=', 'p', - 'SourceForge project shortname'), - ('sf-prikey=', 'k', - 'SourceForge private key filename'), - ] - - def initialize_options(self): - self.sf_user = None - self.sf_project = None - self.sf_prikey = None - - def finalize_options(self): - pass - - def run(self): - import paramiko - ssh = paramiko.SSHClient() - host = 'frs.sourceforge.net' - username='%s,%s' % (self.sf_user, self.sf_project) - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - pkey = None - if self.sf_prikey: - for ktype in paramiko.RSAKey, paramiko.DSSKey: - try: - pkey = ktype.from_private_key_file(self.sf_prikey) - except paramiko.PasswordRequiredException: - pkey = ktype.from_private_key_file( - self.sf_prikey, getpass('Password for %s: ' % self.sf_prikey)) - except paramiko.SSHException: - pass - if pkey: - ssh.connect( - host, - username=username, - pkey=pkey, - look_for_keys=False, - allow_agent=False) - else: - ssh.connect( - host, - username=username, - password=getpass('Password:'), - pkey=pkey, - look_for_keys=False, - allow_agent=False) - - sftp = ssh.open_sftp() - shortname = self.sf_project - release = self.distribution.get_version() - download_url_tpl = string.Template( - 'http://downloads.sourceforge.net/project/$shortname/$release/$basename') - sftp.chdir('/home/frs/project/%s/%s/%s' % ( - shortname[0], shortname[:2], shortname)) - if release not in sftp.listdir(): - sftp.mkdir(release) - for cmd, _, filename in self.distribution.dist_files: - basename = os.path.basename(filename) - sftp.put(filename, '%s/%s' % (release, basename)) - self.distribution.metadata.download_url = download_url_tpl.substitute(locals()) - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/metadata.pyi new/Ming-0.11.2/ming/metadata.pyi --- old/Ming-0.10.3/ming/metadata.pyi 1970-01-01 01:00:00.000000000 +0100 +++ new/Ming-0.11.2/ming/metadata.pyi 2021-10-15 17:17:48.000000000 +0200 @@ -0,0 +1,220 @@ +from bson import ObjectId +from datetime import datetime +from typing import Generic, TypeVar, Any, Optional, overload, List, Dict, Type, Union, Mapping, type_check_only + +import bson +from ming.base import Cursor + +from . import schema +from .base import Object + + +AnyType = TypeVar('AnyType') + + +@type_check_only +class DeprecatedField: + YOU_SHOULDNT_USE_THIS = ... + + +# if https://youtrack.jetbrains.com/issue/PY-26184 is implemented, we could do a more technically "correct" type definition +# using descriptors, but the above __new__ overloads are working quite well, at least for pycharm (not sure about mypy) +#def __get__(self, instance: Any, owner: Any) -> FT: ... + +# none of these seem to work, so instead using @overload with every type explicitly. Which does work quite well +# FT = TypeVar('FT', str, datetime, SchemaItem, List, Dict) +# FT = TypeVar('FT', bound=SchemaItem) +# FT = TypeVar('FT', bound=type) +# FT = TypeVar('FT', int, str, float, bool, datetime) # see SHORTHAND +# class Field(Generic[FT]): + # @overload + # def __new__(cls, type_: FT, **kwargs) -> FT: ... + +class Field: + # see ming.schema.SchemaItem.make() docstring + + # main types like: + # Field(str) + # Field(S.String) + # Field(S.String(if_missing='')) + @overload + def __new__(cls, type_: Type[schema.String], **kwargs) -> str: ... + @overload + def __new__(cls, type_: schema.String, **kwargs) -> str: ... + @overload + def __new__(cls, type_: Type[str], **kwargs) -> str: ... + @overload + def __new__(cls, type_: Type[schema.Int], **kwargs) -> int: ... + @overload + def __new__(cls, type_: schema.Int, **kwargs) -> int: ... + @overload + def __new__(cls, type_: Type[int], **kwargs) -> int: ... + @overload + def __new__(cls, type_: Type[schema.Float], **kwargs) -> Union[int, float]: ... + @overload + def __new__(cls, type_: schema.Float, **kwargs) -> Union[int, float]: ... + @overload + def __new__(cls, type_: Type[float], **kwargs) -> float: ... + @overload + def __new__(cls, type_: Type[schema.DateTimeTZ], **kwargs) -> datetime: ... + @overload + def __new__(cls, type_: schema.DateTimeTZ, **kwargs) -> datetime: ... + @overload + def __new__(cls, type_: Type[datetime], **kwargs) -> datetime: ... + @overload + def __new__(cls, type_: Type[schema.Bool], **kwargs) -> bool: ... + @overload + def __new__(cls, type_: schema.Bool, **kwargs) -> bool: ... + @overload + def __new__(cls, type_: Type[bool], **kwargs) -> bool: ... + + # special + @overload + def __new__(cls, type_: Type[schema.Binary], **kwargs) -> Union[bson.Binary, bytes]: ... + @overload + def __new__(cls, type_: schema.Binary, **kwargs) -> Union[bson.Binary, bytes]: ... + @overload + def __new__(cls, type_: Type[schema.ObjectId], **kwargs) -> bson.ObjectId: ... + @overload + def __new__(cls, type_: schema.ObjectId, **kwargs) -> bson.ObjectId: ... + @overload + def __new__(cls, type_: Type[schema.Anything], **kwargs) -> Any: ... + @overload + def __new__(cls, type_: schema.Anything, **kwargs) -> Any: ... + @overload + def __new__(cls, type_: Type[None], **kwargs) -> Any: ... + @overload + def __new__(cls, type_: Type[schema.Deprecated], **kwargs) -> DeprecatedField: ... + @overload + def __new__(cls, type_: schema.Deprecated, **kwargs) -> DeprecatedField: ... + @overload + def __new__(cls, type_: schema.OneOf, **kwargs) -> Any: ... # unfortunately I don't think we can be more specific + + # container data structures + @overload + def __new__(cls, type_: Type[schema.Array], **kwargs) -> List: ... + @overload + def __new__(cls, type_: schema.Array, **kwargs) -> List: ... + # TODO: typing of elements within an S.Array(str) for example. make Array a generic? + # @overload + # def __new__(cls, type_: schema.Array[str], **kwargs) -> List[str]: ... + @overload + def __new__(cls, type_: List, **kwargs) -> List: ... + @overload + def __new__(cls, type_: List[Type[AnyType]], **kwargs) -> List[AnyType]: ... + @overload + def __new__(cls, type_: Dict, **kwargs) -> Dict: ... + + # repeat all with the actual name first + # like Field('thefieldname', str) + @overload + def __new__(cls, name: str, type_: Type[schema.String], **kwargs) -> str: ... + @overload + def __new__(cls, name: str, type_: schema.String, **kwargs) -> str: ... + @overload + def __new__(cls, name: str, type_: Type[str], **kwargs) -> str: ... + @overload + def __new__(cls, name: str, type_: Type[schema.Int], **kwargs) -> int: ... + @overload + def __new__(cls, name: str, type_: schema.Int, **kwargs) -> int: ... + @overload + def __new__(cls, name: str, type_: Type[int], **kwargs) -> int: ... + @overload + def __new__(cls, name: str, type_: Type[schema.Float], **kwargs) -> Union[int, float]: ... + @overload + def __new__(cls, name: str, type_: schema.Float, **kwargs) -> Union[int, float]: ... + @overload + def __new__(cls, name: str, type_: Type[float], **kwargs) -> float: ... + @overload + def __new__(cls, name: str, type_: Type[schema.DateTimeTZ], **kwargs) -> datetime: ... + @overload + def __new__(cls, name: str, type_: schema.DateTimeTZ, **kwargs) -> datetime: ... + @overload + def __new__(cls, name: str, type_: Type[datetime], **kwargs) -> datetime: ... + @overload + def __new__(cls, name: str, type_: Type[schema.Bool], **kwargs) -> bool: ... + @overload + def __new__(cls, name: str, type_: schema.Bool, **kwargs) -> bool: ... + @overload + def __new__(cls, name: str, type_: Type[bool], **kwargs) -> bool: ... + + # special + @overload + def __new__(cls, name: str, type_: Type[schema.Binary], **kwargs) -> Union[bson.Binary, bytes]: ... + @overload + def __new__(cls, name: str, type_: schema.Binary, **kwargs) -> Union[bson.Binary, bytes]: ... + @overload + def __new__(cls, name: str, type_: Type[schema.ObjectId], **kwargs) -> bson.ObjectId: ... + @overload + def __new__(cls, name: str, type_: schema.ObjectId, **kwargs) -> bson.ObjectId: ... + @overload + def __new__(cls, name: str, type_: Type[schema.Anything], **kwargs) -> Any: ... + @overload + def __new__(cls, name: str, type_: schema.Anything, **kwargs) -> Any: ... + @overload + def __new__(cls, name: str, type_: Type[None], **kwargs) -> Any: ... + @overload + def __new__(cls, name: str, type_: Type[schema.Deprecated], **kwargs) -> DeprecatedField: ... + @overload + def __new__(cls, name: str, type_: schema.Deprecated, **kwargs) -> DeprecatedField: ... + @overload + def __new__(cls, name: str, type_: schema.OneOf, **kwargs) -> Any: ... # unfortunately I don't think we can be more specific + + # container data structures + @overload + def __new__(cls, name: str, type_: Type[schema.Array], **kwargs) -> List: ... + @overload + def __new__(cls, name: str, type_: schema.Array, **kwargs) -> List: ... + @overload + def __new__(cls, name: str, type_: List, **kwargs) -> List: ... + @overload + def __new__(cls, name: str, type_: List[Type[AnyType]], **kwargs) -> List[AnyType]: ... + @overload + def __new__(cls, name: str, type_: Dict, **kwargs) -> Dict: ... + + +M = TypeVar('M') + +MongoFilter = dict +ChangeResult = dict +SaveResult = Union[ObjectId, Any] +class _ClassManager(Generic[M]): + # proxies these from Session + def get(self, **kwargs) -> Optional[M]: ... + def find(self, filter: MongoFilter = None, *args, **kwargs) -> Cursor[M]: ... + #@overload + #def find(self, filter: MongoFilter = None, *args, validate: Literal[False], **kwargs) -> Generator[M]: ... + #@overload + def find_by(self, filter: MongoFilter, *args, validate: bool = True, **kwargs) -> Cursor[M]: ... + #@overload + #def find_by(self, filter: MongoFilter, *args, validate: Literal[False], **kwargs) -> Generator[M]: ... + def remove(self, spec_or_id: Union[MongoFilter, ObjectId] = None, **kwargs) -> ChangeResult: ... + def count(self) -> int: ... + """ + def update_partial(self) -> int: ... + def group(self) -> int: ... + def ensure_indexes(self) -> int: ... + def index_information(self) -> int: ... + def drop_indexes(self) -> int: ... + def find_and_modify(self) -> int: ... + def aggregate(self) -> int: ... + def distinct(self) -> int: ... + def map_reduce(self) -> int: ... + def inline_map_reduce(self) -> int: ... + """ + +class _InstanceManager: + # proxies these from Session + def save(self, *args: str, **kwargs) -> SaveResult: ... + def insert(self, **kwargs) -> SaveResult: ... + def upsert(self, spec_fields: List[str], **kwargs) -> ChangeResult: ... + def delete(self) -> ChangeResult: ... + def set(self, fields_values: Mapping[str, Any]) -> ChangeResult: ... + def increase_field(self, **kwargs) -> None: ... + + +@type_check_only +class Manager(_ClassManager[M], _InstanceManager): ... + + +def __getattr__(name) -> Any: ... # marks file as incomplete \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/mim.py new/Ming-0.11.2/ming/mim.py --- old/Ming-0.10.3/ming/mim.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/mim.py 2021-10-15 17:17:48.000000000 +0200 @@ -5,11 +5,13 @@ import sys import time import itertools +from itertools import chain import collections import logging import warnings from datetime import datetime from hashlib import md5 +from functools import cmp_to_key import pickle @@ -19,33 +21,6 @@ except ImportError: Runtime = None -try: - from functools import cmp_to_key -except ImportError: # Python 2.6 - def cmp_to_key(mycmp): - class K(object): - def __init__(self, obj, *args): - self.obj = obj - - def __lt__(self, other): - return mycmp(self.obj, other.obj) < 0 - - def __gt__(self, other): - return mycmp(self.obj, other.obj) > 0 - - def __eq__(self, other): - return mycmp(self.obj, other.obj) == 0 - - def __le__(self, other): - return mycmp(self.obj, other.obj) <= 0 - - def __ge__(self, other): - return mycmp(self.obj, other.obj) >= 0 - - def __ne__(self, other): - return mycmp(self.obj, other.obj) != 0 - return K - from ming import compat from ming.utils import LazyProperty @@ -187,7 +162,8 @@ collection = self._collections[command['distinct']] key = command['key'] filter = command.get('filter') - return list(set(_lookup(d, key) for d in collection.find(filter=filter))) + all_vals = chain.from_iterable(_lookup(d, key) for d in collection.find(filter=filter)) + return sorted(set(all_vals)) elif 'getlasterror' in command: return dict(connectionId=None, err=None, n=0, ok=1.0) elif 'collstats' in command: @@ -349,8 +325,8 @@ self._name = name self._database = database self._data = {} - self._unique_indexes = {} - self._indexes = {} + self._unique_indexes = {} # name -> doc index {key_values -> _id} + self._indexes = {} # name -> dict of details (including 'key' entry) def __repr__(self): return "mim.Collection(%r, %r)" % (self._database, self.__name) @@ -594,10 +570,13 @@ self._indexes[index_name].update(kwargs) if not unique: return self._indexes[index_name]['unique'] = True - self._unique_indexes[keys] = index = {} + self._unique_indexes[index_name] = docindex = {} + + # update the document index with any existing records for id, doc in six.iteritems(self._data): key_values = self._extract_index_key(doc, keys) - index[key_values] =id + docindex[key_values] = id + return index_name # ensure_index is now deprecated. @@ -610,10 +589,8 @@ for index_name, fields in six.iteritems(self._indexes)) def drop_index(self, iname): - index = self._indexes.pop(iname, None) - if index is None: return - keys = tuple(i[0] for i in index) - self._unique_indexes.pop(keys, None) + self._indexes.pop(iname, None) + self._unique_indexes.pop(iname, None) def drop_indexes(self): for iname in list(self._indexes.keys()): @@ -633,20 +610,26 @@ key_values.append(sub.get(key, None)) return bson.BSON.encode({'k': key_values}) + _null_index_key = bson.BSON.encode({'k': [None]}) + def _index(self, doc): if '_id' not in doc: return - for keys, index in six.iteritems(self._unique_indexes): - key_values = self._extract_index_key(doc, keys) - old_id = index.get(key_values, ()) + for iname, docindex in six.iteritems(self._unique_indexes): + idx_info = self._indexes[iname] + key_values = self._extract_index_key(doc, idx_info['key']) + if idx_info.get('sparse') and key_values == self._null_index_key: + continue + old_id = docindex.get(key_values, ()) if old_id == doc['_id']: continue if old_id in self._data: - raise DuplicateKeyError('%r: %s' % (self, keys)) - index[key_values] = doc['_id'] + raise DuplicateKeyError('%r: %s' % (self, idx_info)) + docindex[key_values] = doc['_id'] def _deindex(self, doc): - for keys, index in six.iteritems(self._unique_indexes): + for iname, docindex in six.iteritems(self._unique_indexes): + keys = self._indexes[iname]['key'] key_values = self._extract_index_key(doc, keys) - index.pop(key_values, None) + docindex.pop(key_values, None) def map_reduce(self, map, reduce, out, full_response=False, **kwargs): if isinstance(out, six.string_types): @@ -807,7 +790,8 @@ return self # I'd rather clone, but that's not what pymongo does here def distinct(self, key): - return list(set(_lookup(d, key) for d in self.all())) + all_vals = chain.from_iterable(_lookup(d, key) for d in self.all()) + return sorted(set(all_vals)) def hint(self, index): # checks indexes, but doesn't actually use hinting @@ -837,8 +821,8 @@ def cursor_comparator(keys): def comparator(a, b): for k,d in keys: - x = _lookup(a, k, None) - y = _lookup(b, k, None) + x = list(_lookup(a, k, None)) + y = list(_lookup(b, k, None)) part = BsonArith.cmp(x, y) if part: return part * d return 0 @@ -1331,12 +1315,20 @@ def _lookup(doc, k, default=()): try: - for part in k.split('.'): - doc = doc[part] + k_parts = k.split('.') + for i, part in enumerate(k_parts): + if isinstance(doc, list): + for item in doc: + remaining_parts = '.'.join(k_parts[i:]) + yield from _lookup(item, remaining_parts, default) + return + else: + doc = doc[part] except KeyError: - if default != (): return default + if default != (): + yield default raise - return doc + yield doc def compare(op, a, b): if op == '$gt': return BsonArith.cmp(a, b) > 0 @@ -1373,7 +1365,7 @@ for k,v in six.iteritems(doc): assert '$' not in k assert '.' not in k - if hasattr(v, 'iteritems'): + if hasattr(v, 'items'): validate(v) def bson_safe(obj): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/odm/base.py new/Ming-0.11.2/ming/odm/base.py --- old/Ming-0.10.3/ming/odm/base.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/odm/base.py 2021-10-15 17:17:48.000000000 +0200 @@ -1,10 +1,18 @@ from copy import deepcopy +import typing -def state(obj): + +if typing.TYPE_CHECKING: + # avoid circular imports + from ming.odm import ODMSession + from ming.odm.unit_of_work import ObjectState + + +def state(obj) -> 'ObjectState': """Gets the UnitOfWork state of a mapped object""" return obj.__ming__.state -def session(v): +def session(v) -> 'ODMSession': """Returns the ORMSession instance managing either a class or an object""" if isinstance(v, type): return v.query.mapper.session diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/odm/declarative.py new/Ming-0.11.2/ming/odm/declarative.py --- old/Ming-0.10.3/ming/odm/declarative.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/odm/declarative.py 2021-09-09 21:37:10.000000000 +0200 @@ -6,11 +6,11 @@ class _MappedClassMeta(type): - def __init__(cls, name, bases, dct): + def __init__(cls, name, bases, dct, **kwargs): cls._registry['%s.%s' % (cls.__module__, cls.__name__)] = mapper(cls) cls._compiled = False - def __new__(meta, name, bases, dct): + def __new__(meta, name, bases, dct, **kwargs): # Get the mapped base class(es) mapped_bases = [b for b in bases if hasattr(b, 'query')] doc_bases = [mapper(b).collection for b in mapped_bases] @@ -42,7 +42,7 @@ properties[k] = v else: clsdict[k] = v - cls = type.__new__(meta, name, bases, clsdict) + cls = type.__new__(meta, name, bases, clsdict, **kwargs) mapper(cls, collection_class, mm.session, properties=properties, include_properties=include_properties, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/odm/declarative.pyi new/Ming-0.11.2/ming/odm/declarative.pyi --- old/Ming-0.10.3/ming/odm/declarative.pyi 1970-01-01 01:00:00.000000000 +0100 +++ new/Ming-0.11.2/ming/odm/declarative.pyi 2021-10-15 17:17:48.000000000 +0200 @@ -0,0 +1,26 @@ +from typing import Any + +from ming.odm.mapper import Query + +class MappedClass: + def __init__(self, **kwargs) -> None: ... + + query: Query[MappedClass] + + """ + in each model class, specify what its manager is, like this. + Its in quotes since Manager isn't a real class and has to be conditionally imported + if typing.TYPE_CHECKING: + from ming.metadata import Manager + + class WikiPage(...): + ... + m: 'Manager[WikiPage]' + """ + + def __getitem__(self, item) -> Any: ... + def __setitem__(self, key, value): ... + def __delitem__(self, key): ... + + +def __getattr__(name) -> Any: ... # marks file as incomplete \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/odm/icollection.py new/Ming-0.11.2/ming/odm/icollection.py --- old/Ming-0.10.3/ming/odm/icollection.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/odm/icollection.py 2021-09-09 21:37:10.000000000 +0200 @@ -162,20 +162,6 @@ self._tracker.removed_item(self._impl[i]) del self._impl[i] - def __setslice__(self, i, j, v): - """Deprecated in Python 2.6""" - v = list(map(deinstrument, v)) - iv = (instrument(item, self._tracker) for item in v) - super(InstrumentedList, self).__getitem__(slice(i, j)) - self._tracker.removed_items(self._impl[i:j]) - self._impl[i:j] = v - self._tracker.added_items(v) - - def __delslice__(self, i, j): - super(InstrumentedList, self).__delslice__(i, j) - self._tracker.removed_items(self._impl[i:j]) - del self._impl[i:j] - def __add__(self, y): return instrument(self._impl + y, self._tracker) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/odm/mapper.py new/Ming-0.11.2/ming/odm/mapper.py --- old/Ming-0.10.3/ming/odm/mapper.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/odm/mapper.py 2021-09-09 21:37:10.000000000 +0200 @@ -68,8 +68,9 @@ @_with_hooks('insert') def insert(self, obj, state, session, **kwargs): doc = self.collection(state.document, skip_from_bson=True) - session.impl.insert(doc, validate=False) + ret = session.impl.insert(doc, validate=False) state.status = state.clean + return ret @_with_hooks('update') def update(self, obj, state, session, **kwargs): @@ -78,17 +79,18 @@ fields = () doc = self.collection(state.document, skip_from_bson=True) - session.impl.save(doc, *fields, validate=False) + ret = session.impl.save(doc, *fields, validate=False) state.status = state.clean + return ret @_with_hooks('delete') def delete(self, obj, state, session, **kwargs): doc = self.collection(state.document, skip_from_bson=True) - session.impl.delete(doc) + return session.impl.delete(doc) @_with_hooks('remove') def remove(self, session, *args, **kwargs): - session.impl.remove(self.collection, *args, **kwargs) + return session.impl.remove(self.collection, *args, **kwargs) def create(self, doc, options, remake=True): if remake is True or type(doc) is not self.collection: @@ -223,11 +225,7 @@ for k in ('__repr__', '__getitem__', '__setitem__', '__contains__', 'delete'): if getattr(self.mapped_class, k, ()) == getattr(object, k, ()): - if six.PY2: - # im_func has been deprecated in Python 3 - setattr(self.mapped_class, k, getattr(inst, k).im_func) - else: - setattr(self.mapped_class, k, getattr(inst, k)) + setattr(self.mapped_class, k, getattr(inst, k)) def _instrumentation(self): class _Instrumentation(object): @@ -242,7 +240,7 @@ 60, indent_subsequent=2) def delete(self_): - self_.query.delete() + return self_.query.delete() def __getitem__(self_, name): try: return getattr(self_, name) @@ -385,7 +383,7 @@ st.status = st.deleted def update(self, fields, **kwargs): - self.classquery.update( + return self.classquery.update( {'_id': self.instance._id }, fields) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/odm/mapper.pyi new/Ming-0.11.2/ming/odm/mapper.pyi --- old/Ming-0.10.3/ming/odm/mapper.pyi 1970-01-01 01:00:00.000000000 +0100 +++ new/Ming-0.11.2/ming/odm/mapper.pyi 2021-09-09 22:21:13.000000000 +0200 @@ -0,0 +1,38 @@ +from typing import type_check_only, TypeVar, Generic, Optional, Union, Any, Dict + +from bson import ObjectId +from ming.base import Cursor + +M = TypeVar('M') + +MongoFilter = dict +ChangeResult = dict +class _ClassQuery(Generic[M]): + # proxies most of these from Session + def get(self, _id: Union[ObjectId|Any] = None, **kwargs) -> Optional[M]: ... + def find(self, filter: MongoFilter = None, *args, **kwargs) -> Cursor[M]: ... + def find_by(self, filter: MongoFilter = None, *args, **kwargs) -> Cursor[M]: ... + def remove(self, spec_or_id: Union[MongoFilter, ObjectId], **kwargs) -> ChangeResult: ... + def count(self) -> int: ... + def find_and_modify(self, **kwargs) -> M: ... + def update(self, spec: MongoFilter, fields: dict, **kwargs) -> ChangeResult: ... + """ + def group(self) -> int: ... + def distinct(self) -> int: ... + def aggregate(self) -> int: ... + def map_reduce(self) -> int: ... + def inline_map_reduce(self) -> int: ... + """ + +class _InstQuery(object): + # proxied from session: + def update_if_not_modified(self, obj, fields, upsert=False) -> bool: ... + + def delete(self) -> ChangeResult: ... + def update(self, fields, **kwargs) -> ChangeResult: ... + +@type_check_only +class Query(_ClassQuery[M], _InstQuery): ... + + +def __getattr__(name) -> Any: ... # marks file as incomplete \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/odm/odmsession.py new/Ming-0.11.2/ming/odm/odmsession.py --- old/Ming-0.10.3/ming/odm/odmsession.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/odm/odmsession.py 2021-09-09 21:37:10.000000000 +0200 @@ -217,7 +217,7 @@ Arguments are the same as :meth:`pymongo.collection.Collection.remove`. """ m = mapper(cls) - m.remove(self, *args, **kwargs) + return m.remove(self, *args, **kwargs) def update(self, cls, spec, fields, **kwargs): """Updates one or more ``cls`` entries from the collection. @@ -230,7 +230,7 @@ Arguments are the same as :meth:`pymongo.collection.Collection.update`. """ m = mapper(cls) - m.update_partial(self, spec, fields, **kwargs) + return m.update_partial(self, spec, fields, **kwargs) def update_if_not_modified(self, obj, fields, upsert=False): """Updates one entry unless it was modified since first queried. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/odm/property.pyi new/Ming-0.11.2/ming/odm/property.pyi --- old/Ming-0.10.3/ming/odm/property.pyi 1970-01-01 01:00:00.000000000 +0100 +++ new/Ming-0.11.2/ming/odm/property.pyi 2021-09-09 22:21:13.000000000 +0200 @@ -0,0 +1,26 @@ + +# re-use all the Field type handling +from typing import overload, Union, Type, Any, TypeVar, TypeAlias + +from bson import ObjectId + +from ming.metadata import Field as FieldProperty +from ming.metadata import Field as FieldPropertyWithMissingNone +from ming.odm.declarative import MappedClass + +MC = TypeVar('MC', bound=MappedClass) + +class ForeignIdProperty: + @overload + def __new__(self, related: Type, uselist=False, allow_none=False, *args, **kwargs) -> ObjectId: ... + @overload + def __new__(self, related: TypeAlias, uselist=False, allow_none=False, *args, **kwargs) -> ObjectId: ... + +class RelationProperty: + @overload + def __new__(self, related: TypeAlias, via: str=None, fetch=True) -> Any:... + @overload + def __new__(self, related: MC, via: str=None, fetch=True) -> MC:... + + +def __getattr__(name) -> Any: ... # marks file as incomplete \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/py.typed new/Ming-0.11.2/ming/py.typed --- old/Ming-0.10.3/ming/py.typed 1970-01-01 01:00:00.000000000 +0100 +++ new/Ming-0.11.2/ming/py.typed 2021-09-09 22:21:13.000000000 +0200 @@ -0,0 +1 @@ +partial diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/session.py new/Ming-0.11.2/ming/session.py --- old/Ming-0.10.3/ming/session.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/session.py 2021-10-01 19:46:00.000000000 +0200 @@ -95,7 +95,7 @@ if kwarg not in ('spec_or_id', 'w'): raise ValueError("Unexpected kwarg %s. Did you mean to pass a dict? If only sent kwargs, pymongo's remove()" " would've emptied the whole collection. Which we're pretty sure you don't want." % kwarg) - self._impl(cls).remove(*args, **kwargs) + return self._impl(cls).remove(*args, **kwargs) def find_by(self, cls, **kwargs): return self.find(cls, kwargs) @@ -161,6 +161,7 @@ result = self._impl(doc).save(data, **fix_write_concern(kwargs)) if result and '_id' not in doc: doc._id = result + return result @annotate_doc_failure def insert(self, doc, **kwargs): @@ -168,19 +169,20 @@ bson = self._impl(doc).insert(data, **fix_write_concern(kwargs)) if bson and '_id' not in doc: doc._id = bson + return bson @annotate_doc_failure def upsert(self, doc, spec_fields, **kwargs): self._prep_save(doc, kwargs.pop('validate', True)) if type(spec_fields) != list: spec_fields = [spec_fields] - self._impl(doc).update(dict((k,doc[k]) for k in spec_fields), + return self._impl(doc).update(dict((k,doc[k]) for k in spec_fields), doc, upsert=True) @annotate_doc_failure def delete(self, doc): - self._impl(doc).remove({'_id':doc._id}) + return self._impl(doc).remove({'_id':doc._id}) def _set(self, doc, key_parts, value): if len(key_parts) == 0: @@ -200,7 +202,7 @@ for k,v in six.iteritems(fields_values): self._set(doc, k.split('.'), v) impl = self._impl(doc) - impl.update({'_id':doc._id}, {'$set':fields_values}) + return impl.update({'_id':doc._id}, {'$set':fields_values}) @annotate_doc_failure def increase_field(self, doc, **kwargs): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/tests/__init__.py new/Ming-0.11.2/ming/tests/__init__.py --- old/Ming-0.10.3/ming/tests/__init__.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/tests/__init__.py 2021-09-09 21:37:10.000000000 +0200 @@ -1,33 +0,0 @@ -from unittest import TestCase - -# Various test suite methods missing on Python2.6 -if not hasattr(TestCase, 'assertIsNotNone'): - def _assertIsNotNone(self, expr, *args): - self.assertTrue(expr is not None, *args) - TestCase.assertIsNotNone = _assertIsNotNone - -if not hasattr(TestCase, 'assertIsNone'): - def _assertIsNone(self, expr, *args): - self.assertTrue(expr is None, *args) - TestCase.assertIsNone = _assertIsNone - -if not hasattr(TestCase, 'assertRaisesRegexp'): - import re - def _assertRaisesRegexp(self, expected_exception, expected_regexp, - callable_obj, *args, **kwargs): - try: - callable_obj(*args, **kwargs) - except expected_exception as exc_value: - if isinstance(expected_regexp, (str, unicode)): - expected_regexp = re.compile(expected_regexp) - if not expected_regexp.search(str(exc_value)): - raise self.failureException( - '"%s" does not match "%s"' % - (expected_regexp.pattern, str(exc_value))) - else: - if hasattr(expected_exception, '__name__'): - excName = expected_exception.__name__ - else: - excName = str(expected_exception) - raise self.failureException("%s not raised" % excName) - TestCase.assertRaisesRegexp = _assertRaisesRegexp diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/tests/odm/test_declarative.py new/Ming-0.11.2/ming/tests/odm/test_declarative.py --- old/Ming-0.10.3/ming/tests/odm/test_declarative.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/tests/odm/test_declarative.py 2021-09-09 22:42:52.000000000 +0200 @@ -1,5 +1,6 @@ +import sys from collections import defaultdict -from unittest import TestCase +from unittest import TestCase, SkipTest from ming import schema as S from ming import create_datastore @@ -67,6 +68,28 @@ u2 = User.query.find({"username": "anonymous"}).first() assert u._id == u2._id + def test_with_init_subclass(self): + if sys.version_info[0:2] < (3, 6): + raise SkipTest('__init_subclass__ not supported before python 3.6') + + class User(MappedClass): + class __mongometa__: + name = "users_with_init_subclass" + session = self.session + + _id = FieldProperty(S.ObjectId) + username = FieldProperty(str) + + def __init_subclass__(cls, constant=1, **kwargs): + super().__init_subclass__(**kwargs) + cls.prop = constant + + class B(User, constant=2): + pass + + b = B() + assert b.prop == 2 + def test_delete_super(self): class User(MappedClass): class __mongometa__: @@ -88,7 +111,7 @@ class TestMappingReal(TestMapping): - DATASTORE = "ming_tests" + DATASTORE = "mongodb://localhost/ming_tests?serverSelectionTimeoutMS=100" class TestRelation(TestCase): @@ -208,7 +231,7 @@ class TestRealMongoRelation(TestRelation): - DATASTORE = "ming_tests" + DATASTORE = "mongodb://localhost/ming_tests?serverSelectionTimeoutMS=100" class TestManyToManyListRelation(TestCase): @@ -706,7 +729,7 @@ class TestRealBasicMapping(TestBasicMapping): - DATASTORE = "test_ming" + DATASTORE = "mongodb://localhost/test_ming?serverSelectionTimeoutMS=100" class TestPolymorphic(TestCase): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/tests/test_base.py new/Ming-0.11.2/ming/tests/test_base.py --- old/Ming-0.10.3/ming/tests/test_base.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/tests/test_base.py 2021-09-09 21:37:10.000000000 +0200 @@ -45,10 +45,6 @@ c = [ 'foo', 1, 1.0, now, Decimal('0.3'), None, oid ] - if six.PY2: - c = [ 'foo', 1, long(1), 1.0, now, - Decimal('0.3'), None, oid ] - safe_obj = Object( a=[1,2,3], b=dict(a=12), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/tests/test_mim.py new/Ming-0.11.2/ming/tests/test_mim.py --- old/Ming-0.10.3/ming/tests/test_mim.py 2021-03-10 15:48:57.000000000 +0100 +++ new/Ming-0.11.2/ming/tests/test_mim.py 2021-10-15 17:17:48.000000000 +0200 @@ -684,6 +684,24 @@ result = self.bind.db.coll.distinct('a') self.assertEqual(result, ['A']) + def test_distinct_subkey(self): + for i in range(5): + self.bind.db.coll.insert({'_id': str(i), 'a': {'b': 'A'}}) + result = self.bind.db.coll.distinct('a.b') + self.assertEqual(result, ['A']) + + def test_distinct_sublist(self): + for i in range(5): + self.bind.db.coll.insert({'_id': str(i), + 'a': [{'b': 'A', 'z': 'z', 'f': {'f': 'F'}}, + {'b': 'C', 'z': 'z', 'f': {'f': 'G'}}]}) + result = self.bind.db.coll.distinct('a.b') + self.assertEqual(result, ['A', 'C']) + result = self.bind.db.coll.distinct('a.z') + self.assertEqual(result, ['z']) + result = self.bind.db.coll.distinct('a.f.f') + self.assertEqual(result, ['F', 'G']) + def test_distinct_filtered(self): for i in range(5): self.bind.db.coll.insert({'_id': i, 'a': 'A'}) @@ -795,6 +813,33 @@ coll.insert({'x': {'y': 2}}) self.assertRaises(DuplicateKeyError, coll.insert, {'x': {'y': 2}}) + def test_unique_sparse_index_subdocument(self): + coll = self.bind.db.coll + + coll.ensure_index([('x.y', 1)], unique=True, sparse=True) + coll.insert({'x': {'y': 1}}) + + # no duplicate key error on these: + coll.insert({'x': {'y': None}}) + coll.insert({'x': {'y': None}}) + coll.insert({'x': {'other': 'field'}}) + coll.insert({'x': {'other': 'field'}}) + # still errors on an existing duplication + self.assertRaises(DuplicateKeyError, coll.insert, {'x': {'y': 1}}) + + def test_unique_sparse_index_whole_sdoc(self): + coll = self.bind.db.coll + + coll.ensure_index([('x', 1)], unique=True, sparse=True) + coll.insert({'x': {'y': 1}}) + # no duplicate key error on these: + coll.insert({'x': None}) + coll.insert({'x': None}) + coll.insert({'other': 'field'}) + coll.insert({'other': 'field'}) + # still errors on an existing duplication + self.assertRaises(DuplicateKeyError, coll.insert, {'x': {'y': 1}}) + def test_delete_many(self): coll = self.bind.db.coll diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/ming/version.py new/Ming-0.11.2/ming/version.py --- old/Ming-0.10.3/ming/version.py 2021-03-10 15:54:33.000000000 +0100 +++ new/Ming-0.11.2/ming/version.py 2021-10-15 17:19:33.000000000 +0200 @@ -1,2 +1,2 @@ -__version_info__ = ('0', '10', '3') +__version_info__ = ('0', '11', '2') __version__ = '.'.join(__version_info__) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/setup.cfg new/Ming-0.11.2/setup.cfg --- old/Ming-0.10.3/setup.cfg 2021-03-10 15:59:42.000000000 +0100 +++ new/Ming-0.11.2/setup.cfg 2021-10-15 17:20:06.000000000 +0200 @@ -1,9 +1,3 @@ -[global] -command-packages = ming.distutils_commands - -[sf_upload] -sf_project = merciless - [nosetests] detailed-errors = 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/Ming-0.10.3/setup.py new/Ming-0.11.2/setup.py --- old/Ming-0.10.3/setup.py 2021-03-10 15:49:18.000000000 +0100 +++ new/Ming-0.11.2/setup.py 2021-09-10 00:29:51.000000000 +0200 @@ -17,15 +17,14 @@ 'Programming Language :: Python', 'Topic :: Database', 'Topic :: Software Development :: Libraries :: Python Modules', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', ], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers + python_requires='>=3.5', keywords='mongo, pymongo', author='Rick Copeland', author_email='[email protected]', ++++++ python-Ming-no-mock.patch ++++++ Index: Ming-0.11.2/ming/tests/odm/test_instrumented_collections.py =================================================================== --- Ming-0.11.2.orig/ming/tests/odm/test_instrumented_collections.py 2020-05-04 22:01:26.000000000 +0200 +++ Ming-0.11.2/ming/tests/odm/test_instrumented_collections.py 2022-03-24 10:56:55.226968156 +0100 @@ -1,6 +1,6 @@ from unittest import TestCase -from mock import Mock +from unittest.mock import Mock from ming.odm.icollection import instrument, deinstrument, InstrumentedObj Index: Ming-0.11.2/ming/tests/odm/test_mapper.py =================================================================== --- Ming-0.11.2.orig/ming/tests/odm/test_mapper.py 2020-06-17 23:36:55.000000000 +0200 +++ Ming-0.11.2/ming/tests/odm/test_mapper.py 2022-03-24 10:56:35.954854123 +0100 @@ -1,6 +1,6 @@ from unittest import TestCase -from mock import Mock, patch +from unittest.mock import Mock, patch from ming import create_datastore from ming import schema as S Index: Ming-0.11.2/ming/tests/test_datastore.py =================================================================== --- Ming-0.11.2.orig/ming/tests/test_datastore.py 2020-06-17 23:36:55.000000000 +0200 +++ Ming-0.11.2/ming/tests/test_datastore.py 2022-03-24 10:56:35.954854123 +0100 @@ -1,7 +1,7 @@ import sys from unittest import TestCase, main -from mock import patch, Mock +from unittest.mock import patch, Mock from pymongo.errors import ConnectionFailure import ming Index: Ming-0.11.2/ming/tests/test_declarative.py =================================================================== --- Ming-0.11.2.orig/ming/tests/test_declarative.py 2020-05-04 22:01:26.000000000 +0200 +++ Ming-0.11.2/ming/tests/test_declarative.py 2022-03-24 10:56:35.954854123 +0100 @@ -1,7 +1,6 @@ -from unittest import TestCase +from unittest import TestCase, mock from collections import defaultdict -import mock import pymongo import six from pymongo.errors import AutoReconnect Index: Ming-0.11.2/ming/tests/test_functional.py =================================================================== --- Ming-0.11.2.orig/ming/tests/test_functional.py 2020-05-04 22:01:26.000000000 +0200 +++ Ming-0.11.2/ming/tests/test_functional.py 2022-03-24 10:56:35.954854123 +0100 @@ -2,10 +2,9 @@ Test the new functional syntax for collection definition ''' -from unittest import TestCase +from unittest import TestCase, mock from collections import defaultdict -import mock import pymongo import six Index: Ming-0.11.2/ming/tests/test_mim.py =================================================================== --- Ming-0.11.2.orig/ming/tests/test_mim.py 2021-10-15 17:17:48.000000000 +0200 +++ Ming-0.11.2/ming/tests/test_mim.py 2022-03-24 10:56:35.954854123 +0100 @@ -6,7 +6,7 @@ import bson from ming import create_datastore, mim from pymongo import UpdateOne from pymongo.errors import OperationFailure, DuplicateKeyError -from mock import patch +from unittest.mock import patch class TestDatastore(TestCase): Index: Ming-0.11.2/ming/tests/test_session.py =================================================================== --- Ming-0.11.2.orig/ming/tests/test_session.py 2020-06-17 23:36:55.000000000 +0200 +++ Ming-0.11.2/ming/tests/test_session.py 2022-03-24 10:56:35.954854123 +0100 @@ -1,7 +1,6 @@ from collections import defaultdict -from unittest import TestCase, main +from unittest import TestCase, main, mock -import mock import bson import pymongo
