A wrapper object that is injected into the ``__globals__`` and ``__closure__`` of a Python function. When the function is instrumented with :class:`.PyWrapper` objects, it is then invoked just once in order to set up the wrappers. We look through all the :class:`.PyWrapper` objects
| 1276 | |
| 1277 | |
| 1278 | class PyWrapper(ColumnOperators): |
| 1279 | """A wrapper object that is injected into the ``__globals__`` and |
| 1280 | ``__closure__`` of a Python function. |
| 1281 | |
| 1282 | When the function is instrumented with :class:`.PyWrapper` objects, it is |
| 1283 | then invoked just once in order to set up the wrappers. We look through |
| 1284 | all the :class:`.PyWrapper` objects we made to find the ones that generated |
| 1285 | a :class:`.BindParameter` object, e.g. the expression system interpreted |
| 1286 | something as a literal. Those positions in the globals/closure are then |
| 1287 | ones that we will look at, each time a new lambda comes in that refers to |
| 1288 | the same ``__code__`` object. In this way, we keep a single version of |
| 1289 | the SQL expression that this lambda produced, without calling upon the |
| 1290 | Python function that created it more than once, unless its other closure |
| 1291 | variables have changed. The expression is then transformed to have the |
| 1292 | new bound values embedded into it. |
| 1293 | |
| 1294 | """ |
| 1295 | |
| 1296 | def __init__( |
| 1297 | self, |
| 1298 | fn, |
| 1299 | name, |
| 1300 | to_evaluate, |
| 1301 | closure_index=None, |
| 1302 | getter=None, |
| 1303 | track_bound_values=True, |
| 1304 | ): |
| 1305 | self.fn = fn |
| 1306 | self._name = name |
| 1307 | self._to_evaluate = to_evaluate |
| 1308 | self._param = None |
| 1309 | self._has_param = False |
| 1310 | self._bind_paths = {} |
| 1311 | self._getter = getter |
| 1312 | self._closure_index = closure_index |
| 1313 | self.track_bound_values = track_bound_values |
| 1314 | |
| 1315 | def __call__(self, *arg, **kw): |
| 1316 | elem = object.__getattribute__(self, "_to_evaluate") |
| 1317 | value = elem(*arg, **kw) |
| 1318 | if ( |
| 1319 | self._sa_track_bound_values |
| 1320 | and coercions._deep_is_literal(value) |
| 1321 | and not isinstance( |
| 1322 | # TODO: coverage where an ORM option or similar is here |
| 1323 | value, |
| 1324 | _cache_key.HasCacheKey, |
| 1325 | ) |
| 1326 | ): |
| 1327 | name = object.__getattribute__(self, "_name") |
| 1328 | raise exc.InvalidRequestError( |
| 1329 | "Can't invoke Python callable %s() inside of lambda " |
| 1330 | "expression argument at %s; lambda SQL constructs should " |
| 1331 | "not invoke functions from closure variables to produce " |
| 1332 | "literal values since the " |
| 1333 | "lambda SQL system normally extracts bound values without " |
| 1334 | "actually " |
| 1335 | "invoking the lambda or any functions within it. Call the " |
no outgoing calls
no test coverage detected