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
 

Reply via email to