1212# See the License for the specific language governing permissions and
1313# limitations under the License.
1414from types import TracebackType
15- from typing import Any , Iterator , List , Mapping , Optional , Sequence , Tuple , Type , Union
15+ from typing import (
16+ Any ,
17+ Callable ,
18+ Iterator ,
19+ List ,
20+ Mapping ,
21+ Optional ,
22+ Sequence ,
23+ Tuple ,
24+ Type ,
25+ Union ,
26+ )
1627
1728from typing_extensions import Protocol
1829
@@ -112,23 +123,45 @@ class DBAPI2Module(Protocol):
112123 # extends from this hierarchy. See
113124 # https://docs.python.org/3/library/sqlite3.html?highlight=sqlite3#exceptions
114125 # https://www.postgresql.org/docs/current/errcodes-appendix.html#ERRCODES-TABLE
115- Warning : Type [Exception ]
116- Error : Type [Exception ]
126+ #
127+ # Note: rather than
128+ # x: T
129+ # we write
130+ # @property
131+ # def x(self) -> T: ...
132+ # which expresses that the protocol attribute `x` is read-only. The mypy docs
133+ # https://mypy.readthedocs.io/en/latest/common_issues.html#covariant-subtyping-of-mutable-protocol-members-is-rejected
134+ # explain why this is necessary for safety. TL;DR: we shouldn't be able to write
135+ # to `x`, only read from it. See also https://github.com/python/mypy/issues/6002 .
136+ @property
137+ def Warning (self ) -> Type [Exception ]:
138+ ...
139+
140+ @property
141+ def Error (self ) -> Type [Exception ]:
142+ ...
117143
118144 # Errors are divided into `InterfaceError`s (something went wrong in the database
119145 # driver) and `DatabaseError`s (something went wrong in the database). These are
120146 # both subclasses of `Error`, but we can't currently express this in type
121147 # annotations due to https://github.com/python/mypy/issues/8397
122- InterfaceError : Type [Exception ]
123- DatabaseError : Type [Exception ]
148+ @property
149+ def InterfaceError (self ) -> Type [Exception ]:
150+ ...
151+
152+ @property
153+ def DatabaseError (self ) -> Type [Exception ]:
154+ ...
124155
125156 # Everything below is a subclass of `DatabaseError`.
126157
127158 # Roughly: the database rejected a nonsensical value. Examples:
128159 # - An integer was too big for its data type.
129160 # - An invalid date time was provided.
130161 # - A string contained a null code point.
131- DataError : Type [Exception ]
162+ @property
163+ def DataError (self ) -> Type [Exception ]:
164+ ...
132165
133166 # Roughly: something went wrong in the database, but it's not within the application
134167 # programmer's control. Examples:
@@ -138,28 +171,45 @@ class DBAPI2Module(Protocol):
138171 # - A serialisation failure occurred.
139172 # - The database ran out of resources, such as storage, memory, connections, etc.
140173 # - The database encountered an error from the operating system.
141- OperationalError : Type [Exception ]
174+ @property
175+ def OperationalError (self ) -> Type [Exception ]:
176+ ...
142177
143178 # Roughly: we've given the database data which breaks a rule we asked it to enforce.
144179 # Examples:
145180 # - Stop, criminal scum! You violated the foreign key constraint
146181 # - Also check constraints, non-null constraints, etc.
147- IntegrityError : Type [Exception ]
182+ @property
183+ def IntegrityError (self ) -> Type [Exception ]:
184+ ...
148185
149186 # Roughly: something went wrong within the database server itself.
150- InternalError : Type [Exception ]
187+ @property
188+ def InternalError (self ) -> Type [Exception ]:
189+ ...
151190
152191 # Roughly: the application did something silly that needs to be fixed. Examples:
153192 # - We don't have permissions to do something.
154193 # - We tried to create a table with duplicate column names.
155194 # - We tried to use a reserved name.
156195 # - We referred to a column that doesn't exist.
157- ProgrammingError : Type [Exception ]
196+ @property
197+ def ProgrammingError (self ) -> Type [Exception ]:
198+ ...
158199
159200 # Roughly: we've tried to do something that this database doesn't support.
160- NotSupportedError : Type [Exception ]
201+ @property
202+ def NotSupportedError (self ) -> Type [Exception ]:
203+ ...
161204
162- def connect (self , ** parameters : object ) -> Connection :
205+ # We originally wrote
206+ # def connect(self, *args, **kwargs) -> Connection: ...
207+ # But mypy doesn't seem to like that because sqlite3.connect takes a mandatory
208+ # positional argument. We can't make that part of the signature though, because
209+ # psycopg2.connect doesn't have a mandatory positional argument. Instead, we use
210+ # the following slightly unusual workaround.
211+ @property
212+ def connect (self ) -> Callable [..., Connection ]:
163213 ...
164214
165215
0 commit comments