class Environment(object): """ An environment wraps data for ORM records: - :attr:`cr`, the current database cursor; - :attr:`uid`, the current user id; - :attr:`context`, the current context dictionary. It provides access to the registry by implementing a mapping from model names to new api models. It also holds a cache for records, and a data structure to manage recomputations. """ _local = Local() @classproperty def envs(cls): return cls._local.environments @classmethod @contextmanager def manage(cls): """ Context manager for a set of environments. """ if hasattr(cls._local, 'environments'): yield else: try: cls._local.environments = Environments() yield finally: release_local(cls._local) @classmethod def reset(cls): """ Clear the set of environments. This may be useful when recreating a registry inside a transaction. """ cls._local.environments = Environments() def __new__(cls, cr, uid, context): assert context is not None args = (cr, uid, context) # if env already exists, return it env, envs = None, cls.envs for env in envs: if env.args == args: return env # otherwise create environment, and add it in the set self = object.__new__(cls) self.cr, self.uid, self.context = self.args = (cr, uid, frozendict(context)) self.registry = RegistryManager.get(cr.dbname) self.cache = defaultdict(dict) # {field: {id: value, ...}, ...} self.prefetch = defaultdict(set) # {model_name: set(id), ...} self.computed = defaultdict(set) # {field: set(id), ...} self.dirty = defaultdict(set) # {record: set(field_name), ...} self.all = envs envs.add(self) return self def __contains__(self, model_name): """ Test whether the given model exists. """ return model_name in self.registry def __getitem__(self, model_name): """ Return an empty recordset from the given model. """ return self.registry[model_name]._browse(self, ()) def __iter__(self): """ Return an iterator on model names. """ return iter(self.registry) def __len__(self): """ Return the size of the model registry. """ return len(self.registry) def __call__(self, cr=None, user=None, context=None): """ Return an environment based on ``self`` with modified parameters. :param cr: optional database cursor to change the current cursor :param user: optional user/user id to change the current user :param context: optional context dictionary to change the current context """ cr = self.cr if cr is None else cr uid = self.uid if user is None else int(user) context = self.context if context is None else context return Environment(cr, uid, context) def ref(self, xml_id, raise_if_not_found=True): """ return the record corresponding to the given ``xml_id`` """ return self['ir.model.data'].xmlid_to_object(xml_id, raise_if_not_found=raise_if_not_found) @property def user(self): """ return the current user (as an instance) """ return self(user=SUPERUSER_ID)['res.users'].browse(self.uid) @property def lang(self): """ return the current language code """ return self.context.get('lang') @contextmanager def _do_in_mode(self, mode): if self.all.mode: yield else: try: self.all.mode = mode yield finally: self.all.mode = False self.dirty.clear() def do_in_draft(self): """ Context-switch to draft mode, where all field updates are done in cache only. """ return self._do_in_mode(True) @property def in_draft(self): """ Return whether we are in draft mode. """ return bool(self.all.mode) def do_in_onchange(self): """ Context-switch to 'onchange' draft mode, which is a specialized draft mode used during execution of onchange methods. """ return self._do_in_mode('onchange') @property def in_onchange(self): """ Return whether we are in 'onchange' draft mode. """ return self.all.mode == 'onchange' def invalidate(self, spec): """ Invalidate some fields for some records in the cache of all environments. :param spec: what to invalidate, a list of `(field, ids)` pair, where ``field`` is a field object, and ``ids`` is a list of record ids or ``None`` (to invalidate all records). """ if not spec: return for env in list(self.all): c = env.cache for field, ids in spec: if ids is None: if field in c: del c[field] else: field_cache = c[field] for id in ids: field_cache.pop(id, None) def invalidate_all(self): """ Clear the cache of all environments. """ for env in list(self.all): env.cache.clear() env.prefetch.clear() env.computed.clear() env.dirty.clear() def clear(self): """ Clear all record caches, and discard all fields to recompute. This may be useful when recovering from a failed ORM operation. """ self.invalidate_all() self.all.todo.clear() @contextmanager def clear_upon_failure(self): """ Context manager that clears the environments (caches and fields to recompute) upon exception. """ try: yield except Exception: self.clear() raise def field_todo(self, field): """ Return a recordset with all records to recompute for ``field``. """ ids = {rid for recs in self.all.todo.get(field, ()) for rid in recs.ids} return self[field.model_name].browse(ids) def check_todo(self, field, record): """ Check whether ``field`` must be recomputed on ``record``, and if so, return the corresponding recordset to recompute. """ for recs in self.all.todo.get(field, []): if recs & record: return recs def add_todo(self, field, records): """ Mark ``field`` to be recomputed on ``records``. """ recs_list = self.all.todo.setdefault(field, []) for i, recs in enumerate(recs_list): if recs.env == records.env: recs_list[i] |= records break else: recs_list.append(records) def remove_todo(self, field, records): """ Mark ``field`` as recomputed on ``records``. """ recs_list = [recs - records for recs in self.all.todo.pop(field, [])] recs_list = filter(None, recs_list) if recs_list: self.all.todo[field] = recs_list def has_todo(self): """ Return whether some fields must be recomputed. """ return bool(self.all.todo) def get_todo(self): """ Return a pair ``(field, records)`` to recompute. The field is such that none of its dependencies must be recomputed. """ field = min(self.all.todo, key=self.registry.field_sequence) return field, self.all.todo[field][0] def check_cache(self): """ Check the cache consistency. """ # make a full copy of the cache, and invalidate it cache_dump = dict( (field, dict(field_cache)) for field, field_cache in self.cache.iteritems() ) self.invalidate_all() # re-fetch the records, and compare with their former cache invalids = [] for field, field_dump in cache_dump.iteritems(): ids = filter(None, field_dump) records = self[field.model_name].browse(ids) for record in records: try: cached = field_dump[record.id] fetched = record[field.name] if fetched != cached: info = {'cached': cached, 'fetched': fetched} invalids.append((field, record, info)) except (AccessError, MissingError): pass if invalids: raise UserError('Invalid cache for fields\n' + pformat(invalids)) @property def recompute(self): return self.all.recompute @contextmanager def norecompute(self): tmp = self.all.recompute self.all.recompute = False try: yield finally: self.all.recompute = tmp @property def recompute_old(self): return self.all.recompute_old def clear_recompute_old(self): del self.all.recompute_old[:]
~~~~~~~~~~~~~~~~~~~~~~ Interactive console support. :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details. :license: BSD. """ import sys import code from types import CodeType from werkzeug.utils import escape from werkzeug.local import Local from werkzeug.debug.repr import debug_repr, dump, helper _local = Local() class HTMLStringO(object): """A StringO version that HTML escapes on write.""" def __init__(self): self._buffer = [] def isatty(self): return False def close(self): pass
class Environment(Mapping): """ An environment wraps data for ORM records: - :attr:`cr`, the current database cursor; - :attr:`uid`, the current user id; - :attr:`context`, the current context dictionary. It provides access to the registry by implementing a mapping from model names to new api models. It also holds a cache for records, and a data structure to manage recomputations. """ _local = Local() @classproperty def envs(cls): return cls._local.environments @classmethod @contextmanager def manage(cls): """ Context manager for a set of environments. """ if hasattr(cls._local, 'environments'): yield else: try: cls._local.environments = Environments() yield finally: release_local(cls._local) @classmethod def reset(cls): """ Clear the set of environments. This may be useful when recreating a registry inside a transaction. """ cls._local.environments = Environments() def __new__(cls, cr, uid, context): assert context is not None args = (cr, uid, context) # if env already exists, return it env, envs = None, cls.envs for env in envs: if env.args == args: return env # otherwise create environment, and add it in the set self = object.__new__(cls) self.cr, self.uid, self.context = self.args = (cr, uid, frozendict(context)) self.registry = Registry(cr.dbname) self.cache = envs.cache self._cache_key = (cr, uid) self._protected = defaultdict(frozenset) # {field: ids, ...} self.dirty = defaultdict(set) # {record: set(field_name), ...} self.all = envs envs.add(self) return self # # Mapping methods # def __contains__(self, model_name): """ Test whether the given model exists. """ return model_name in self.registry def __getitem__(self, model_name): """ Return an empty recordset from the given model. """ return self.registry[model_name]._browse((), self) def __iter__(self): """ Return an iterator on model names. """ return iter(self.registry) def __len__(self): """ Return the size of the model registry. """ return len(self.registry) def __eq__(self, other): return self is other def __ne__(self, other): return self is not other def __hash__(self): return object.__hash__(self) def __call__(self, cr=None, user=None, context=None): """ Return an environment based on ``self`` with modified parameters. :param cr: optional database cursor to change the current cursor :param user: optional user/user id to change the current user :param context: optional context dictionary to change the current context """ cr = self.cr if cr is None else cr uid = self.uid if user is None else int(user) context = self.context if context is None else context return Environment(cr, uid, context) def ref(self, xml_id, raise_if_not_found=True): """ return the record corresponding to the given ``xml_id`` """ return self['ir.model.data'].xmlid_to_object( xml_id, raise_if_not_found=raise_if_not_found) @property def user(self): """ return the current user (as an instance) """ return self(user=SUPERUSER_ID)['res.users'].browse(self.uid) @property def lang(self): """ return the current language code """ return self.context.get('lang') @contextmanager def _do_in_mode(self, mode): if self.all.mode: yield else: try: self.all.mode = mode yield finally: self.all.mode = False self.dirty.clear() def do_in_draft(self): """ Context-switch to draft mode, where all field updates are done in cache only. """ return self._do_in_mode(True) @property def in_draft(self): """ Return whether we are in draft mode. """ return bool(self.all.mode) def do_in_onchange(self): """ Context-switch to 'onchange' draft mode, which is a specialized draft mode used during execution of onchange methods. """ return self._do_in_mode('onchange') @property def in_onchange(self): """ Return whether we are in 'onchange' draft mode. """ return self.all.mode == 'onchange' def clear(self): """ Clear all record caches, and discard all fields to recompute. This may be useful when recovering from a failed ORM operation. """ self.cache.invalidate() self.all.todo.clear() @contextmanager def clear_upon_failure(self): """ Context manager that clears the environments (caches and fields to recompute) upon exception. """ try: yield except Exception: self.clear() raise def protected(self, field): """ Return the recordset for which ``field`` should not be invalidated or recomputed. """ return self[field.model_name].browse(self._protected.get(field, ())) @contextmanager def protecting(self, fields, records): """ Prevent the invalidation or recomputation of ``fields`` on ``records``. """ saved = {} try: for field in fields: ids = saved[field] = self._protected[field] self._protected[field] = ids.union(records._ids) yield finally: self._protected.update(saved) def field_todo(self, field): """ Return a recordset with all records to recompute for ``field``. """ ids = { rid for recs in self.all.todo.get(field, ()) for rid in recs.ids } return self[field.model_name].browse(ids) def check_todo(self, field, record): """ Check whether ``field`` must be recomputed on ``record``, and if so, return the corresponding recordset to recompute. """ for recs in self.all.todo.get(field, []): if recs & record: return recs def add_todo(self, field, records): """ Mark ``field`` to be recomputed on ``records``. """ recs_list = self.all.todo.setdefault(field, []) for i, recs in enumerate(recs_list): if recs.env == records.env: recs_list[i] |= records break else: recs_list.append(records) def remove_todo(self, field, records): """ Mark ``field`` as recomputed on ``records``. """ recs_list = [recs - records for recs in self.all.todo.pop(field, [])] recs_list = [r for r in recs_list if r] if recs_list: self.all.todo[field] = recs_list def has_todo(self): """ Return whether some fields must be recomputed. """ return bool(self.all.todo) def get_todo(self): """ Return a pair ``(field, records)`` to recompute. The field is such that none of its dependencies must be recomputed. """ field = min(self.all.todo, key=self.registry.field_sequence) return field, self.all.todo[field][0] @property def recompute(self): return self.all.recompute @contextmanager def norecompute(self): tmp = self.all.recompute self.all.recompute = False try: yield finally: self.all.recompute = tmp def cache_key(self, field): """ Return the key to store the value of ``field`` in cache, the full cache key being ``(key, field, record.id)``. """ return self if field.context_dependent else self._cache_key
@Date : 2019/3/1 15:00 @Desc : """ import threading import time from werkzeug.local import Local __author__ = "GaoZizhong" class A: b = 1 my_obj = Local() my_obj.b = 1 def worker(): # 新线程 my_obj.b = 2 print("In new thread b is" + str(my_obj.b)) new_t = threading.Thread(target=worker, name=__author__ + "_thread") new_t.start() time.sleep(1) # 主线程 print("In main thread b is" + str(my_obj.b))
from flask import Flask, current_app import threading import time from werkzeug.local import Local t = threading.current_thread() print(t.getName()) class A: t = 1 l = Local() l.b = 1 def worker(): t = threading.current_thread() # print(t.getName()) l.b = 2 time.sleep(7) print('hello') t1 = threading.Thread(target=worker, daemon=True) t1.start() print(l.b) # t1.join()
def init_app( self, app, force_https=True, force_https_permanent=False, force_file_save=False, frame_options=SAMEORIGIN, frame_options_allow_from=None, strict_transport_security=True, strict_transport_security_preload=False, strict_transport_security_max_age=ONE_YEAR_IN_SECS, strict_transport_security_include_subdomains=True, content_security_policy=DEFAULT_CSP_POLICY, content_security_policy_report_uri=None, content_security_policy_report_only=False, session_cookie_secure=True, session_cookie_http_only=True): """ Initialization. Args: app: A Flask application. force_https: Redirects non-http requests to https, disabled in debug mode. force_https_permanent: Uses 301 instead of 302 redirects. frame_options: Sets the X-Frame-Options header, defaults to SAMEORIGIN. frame_options_allow_from: Used when frame_options is set to ALLOW_FROM and is a string of domains to allow frame embedding. strict_transport_security: Sets HSTS headers. strict_transport_security_preload: Enables HSTS preload. See https://hstspreload.org. strict_transport_security_max_age: How long HSTS headers are honored by the browser. strict_transport_security_include_subdomain: Whether to include all subdomains when setting HSTS. content_security_policy: A string or dictionary describing the content security policy for the response. content_security_policy_report_uri: A string indicating the report URI used for CSP violation reports content_security_policy_report_only: Whether to set the CSP header as "report-only", which disables the enforcement by the browser and requires a "report-uri" parameter with a backend to receive the POST data session_cookie_secure: Forces the session cookie to only be sent over https. Disabled in debug mode. session_cookie_http_only: Prevents JavaScript from reading the session cookie. force_file_save: Prevents the user from opening a file download directly on >= IE 8 See README.rst for a detailed description of each option. """ self.force_https = force_https self.force_https_permanent = force_https_permanent self.frame_options = frame_options self.frame_options_allow_from = frame_options_allow_from self.strict_transport_security = strict_transport_security self.strict_transport_security_preload = False self.strict_transport_security_max_age = \ strict_transport_security_max_age self.strict_transport_security_include_subdomains = \ strict_transport_security_include_subdomains self.content_security_policy = content_security_policy.copy() self.content_security_policy_report_uri = \ content_security_policy_report_uri self.content_security_policy_report_only = \ content_security_policy_report_only if self.content_security_policy_report_only and \ self.content_security_policy_report_uri is None: raise ValueError( 'Setting content_security_policy_report_only to True also ' 'requires a URI to be specified in ' 'content_security_policy_report_uri') self.session_cookie_secure = session_cookie_secure if session_cookie_http_only: app.config['SESSION_COOKIE_HTTPONLY'] = True self.force_file_save = force_file_save self.app = app self.local_options = Local() app.before_request(self._update_local_options) app.before_request(self._force_https) app.after_request(self._set_response_headers)
#!/usr/bin/env python # -*- coding: utf-8 -*- """ @author: sun @license: (C) Copyright 2016-2019, Light2Cloud (Beijing) Web Service Co., LTD @contact: [email protected] @software: L2CloudCMP @file: local.py @ide: PyCharm @time: 2019/12/19 15:55 @desc: """ from werkzeug.local import Local thread_local = Local() def _find(attr): return getattr(thread_local, attr, None)
default_want_json, get_config, hash_data, localize_callback, send_mail, string_types, url_for_security, verify_and_update_password, verify_hash, ) from .views import create_blueprint, default_render_json from .cache import VerifyHashCache # Convenient references _security = LocalProxy(lambda: current_app.extensions["security"]) local_cache = Local() # List of authentication mechanisms supported. AUTHN_MECHANISMS = ("basic", "session", "token") #: Default Flask-Security configuration _default_config = { "BLUEPRINT_NAME": "security", "CLI_ROLES_NAME": "roles", "CLI_USERS_NAME": "users", "URL_PREFIX": None, "SUBDOMAIN": None, "FLASH_MESSAGES": True, "I18N_DOMAIN": "flask_security", "I18N_DIRNAME": pkg_resources.resource_filename("flask_security", "translations"),
class Environment(Mapping): """ An environment wraps data for ORM records: - :attr:`cr`, the current database cursor; - :attr:`uid`, the current user id; - :attr:`context`, the current context dictionary; - :attr:`su`, whether in superuser mode. It provides access to the registry by implementing a mapping from model names to new api models. It also holds a cache for records, and a data structure to manage recomputations. """ _local = Local() @classproperty def envs(cls): return getattr(cls._local, 'environments', ()) @classmethod @contextmanager def manage(cls): """ Context manager for a set of environments. """ if hasattr(cls._local, 'environments'): yield else: try: cls._local.environments = Environments() yield finally: release_local(cls._local) @classmethod def reset(cls): """ Clear the set of environments. This may be useful when recreating a registry inside a transaction. """ cls._local.environments = Environments() def __new__(cls, cr, uid, context, su=False): if uid == SUPERUSER_ID: su = True assert context is not None args = (cr, uid, context, su) # if env already exists, return it env, envs = None, cls.envs for env in envs: if env.args == args: return env # otherwise create environment, and add it in the set self = object.__new__(cls) args = (cr, uid, frozendict(context), su) self.cr, self.uid, self.context, self.su = self.args = args self.registry = Registry(cr.dbname) self.cache = envs.cache self._cache_key = {} # memo {field: cache_key} self._protected = envs.protected # proxy to shared data structure self.all = envs envs.add(self) return self # # Mapping methods # def __contains__(self, model_name): """ Test whether the given model exists. """ return model_name in self.registry def __getitem__(self, model_name): """ Return an empty recordset from the given model. """ return self.registry[model_name]._browse(self, (), ()) def __iter__(self): """ Return an iterator on model names. """ return iter(self.registry) def __len__(self): """ Return the size of the model registry. """ return len(self.registry) def __eq__(self, other): return self is other def __ne__(self, other): return self is not other def __hash__(self): return object.__hash__(self) def __call__(self, cr=None, user=None, context=None, su=None): """ Return an environment based on ``self`` with modified parameters. :param cr: optional database cursor to change the current cursor :param user: optional user/user id to change the current user :param context: optional context dictionary to change the current context :param su: optional boolean to change the superuser mode :type context: dict :type user: int or :class:`~odoo.addons.base.models.res_users` :type su: bool """ cr = self.cr if cr is None else cr uid = self.uid if user is None else int(user) context = self.context if context is None else context su = (user is None and self.su) if su is None else su return Environment(cr, uid, context, su) def ref(self, xml_id, raise_if_not_found=True): """Return the record corresponding to the given ``xml_id``.""" return self['ir.model.data'].xmlid_to_object( xml_id, raise_if_not_found=raise_if_not_found) def is_superuser(self): """ Return whether the environment is in superuser mode. """ return self.su def is_admin(self): """ Return whether the current user has group "Access Rights", or is in superuser mode. """ return self.su or self.user._is_admin() def is_system(self): """ Return whether the current user has group "Settings", or is in superuser mode. """ return self.su or self.user._is_system() @lazy_property def user(self): """Return the current user (as an instance). :returns: current user - sudoed :rtype: :class:`~odoo.addons.base.models.res_users`""" return self(su=True)['res.users'].browse(self.uid) @lazy_property def company(self): """Return the current company (as an instance). If not specified in the context (`allowed_company_ids`), fallback on current user main company. :raise AccessError: invalid or unauthorized `allowed_company_ids` context key content. :return: current company (default=`self.user.company_id`), with the current environment :rtype: res.company .. warning:: No sanity checks applied in sudo mode ! When in sudo mode, a user can access any company, even if not in his allowed companies. This allows to trigger inter-company modifications, even if the current user doesn't have access to the targeted company. """ company_ids = self.context.get('allowed_company_ids', []) if company_ids: if not self.su: user_company_ids = self.user.company_ids.ids if any(cid not in user_company_ids for cid in company_ids): raise AccessError( _("Access to unauthorized or invalid companies.")) return self['res.company'].browse(company_ids[0]) return self.user.company_id.with_env(self) @lazy_property def companies(self): """Return a recordset of the enabled companies by the user. If not specified in the context(`allowed_company_ids`), fallback on current user companies. :raise AccessError: invalid or unauthorized `allowed_company_ids` context key content. :return: current companies (default=`self.user.company_ids`), with the current environment :rtype: res.company .. warning:: No sanity checks applied in sudo mode ! When in sudo mode, a user can access any company, even if not in his allowed companies. This allows to trigger inter-company modifications, even if the current user doesn't have access to the targeted company. """ company_ids = self.context.get('allowed_company_ids', []) if company_ids: if not self.su: user_company_ids = self.user.company_ids.ids if any(cid not in user_company_ids for cid in company_ids): raise AccessError( _("Access to unauthorized or invalid companies.")) return self['res.company'].browse(company_ids) # By setting the default companies to all user companies instead of the main one # we save a lot of potential trouble in all "out of context" calls, such as # /mail/redirect or /web/image, etc. And it is not unsafe because the user does # have access to these other companies. The risk of exposing foreign records # (wrt to the context) is low because all normal RPCs will have a proper # allowed_company_ids. # Examples: # - when printing a report for several records from several companies # - when accessing to a record from the notification email template # - when loading an binary image on a template return self.user.company_ids.with_env(self) @property def lang(self): """Return the current language code. :rtype: str """ return self.context.get('lang') def clear(self): """ Clear all record caches, and discard all fields to recompute. This may be useful when recovering from a failed ORM operation. """ lazy_property.reset_all(self) self.cache.invalidate() self.all.tocompute.clear() self.all.towrite.clear() @contextmanager def clear_upon_failure(self): """ Context manager that clears the environments (caches and fields to recompute) upon exception. """ tocompute = { field: set(ids) for field, ids in self.all.tocompute.items() } towrite = { model: { record_id: dict(values) for record_id, values in id_values.items() } for model, id_values in self.all.towrite.items() } try: yield except Exception: self.clear() self.all.tocompute.update(tocompute) for model, id_values in towrite.items(): for record_id, values in id_values.items(): self.all.towrite[model][record_id].update(values) raise def is_protected(self, field, record): """ Return whether `record` is protected against invalidation or recomputation for `field`. """ return record.id in self._protected.get(field, ()) def protected(self, field): """ Return the recordset for which ``field`` should not be invalidated or recomputed. """ return self[field.model_name].browse(self._protected.get(field, ())) @contextmanager def protecting(self, what, records=None): """ Prevent the invalidation or recomputation of fields on records. The parameters are either: - ``what`` a collection of fields and ``records`` a recordset, or - ``what`` a collection of pairs ``(fields, records)``. """ protected = self._protected try: protected.pushmap() what = what if records is None else [(what, records)] for fields, records in what: for field in fields: ids = protected.get(field, frozenset()) protected[field] = ids.union(records._ids) yield finally: protected.popmap() def fields_to_compute(self): """ Return a view on the field to compute. """ return self.all.tocompute.keys() def records_to_compute(self, field): """ Return the records to compute for ``field``. """ ids = self.all.tocompute.get(field, ()) return self[field.model_name].browse(ids) def is_to_compute(self, field, record): """ Return whether ``field`` must be computed on ``record``. """ return record.id in self.all.tocompute.get(field, ()) def not_to_compute(self, field, records): """ Return the subset of ``records`` for which ``field`` must not be computed. """ ids = self.all.tocompute.get(field, ()) return records.browse(id_ for id_ in records._ids if id_ not in ids) def add_to_compute(self, field, records): """ Mark ``field`` to be computed on ``records``. """ if not records: return records self.all.tocompute[field].update(records._ids) def remove_to_compute(self, field, records): """ Mark ``field`` as computed on ``records``. """ if not records: return ids = self.all.tocompute.get(field, None) if ids is None: return ids.difference_update(records._ids) if not ids: del self.all.tocompute[field] @contextmanager def norecompute(self): """ Delay recomputations (deprecated: this is not the default behavior). """ yield def cache_key(self, field): """ Return the cache key corresponding to ``field.depends_context``. """ try: return self._cache_key[field] except KeyError: def get(key, get_context=self.context.get): if key == 'company': return self.company.id elif key == 'uid': return (self.uid, self.su) elif key == 'active_test': return get_context('active_test', field.context.get('active_test', True)) else: val = get_context(key) if type(val) is list: val = tuple(val) try: hash(val) except TypeError: raise TypeError( "Can only create cache keys from hashable values, " "got non-hashable value {!r} at context key {!r} " "(dependency of field {})".format(val, key, field) ) from None # we don't need to chain the exception created 2 lines above else: return val result = tuple(get(key) for key in field.depends_context) self._cache_key[field] = result return result
return length def int_length(i): return len(str(i)) def _get_trans(): gettext.install(APP_NAME, LOCALE_DIR) zh = gettext.translation(APP_NAME, LOCALE_DIR, ["zh_CN"]) en = gettext.translation(APP_NAME, LOCALE_DIR, ["en"]) return zh, en trans_zh, trans_en = _get_trans() _thread_locals = Local() def set_current_lang(lang): setattr(_thread_locals, 'LANGUAGE_CODE', lang) def get_current_lang(attr): return getattr(_thread_locals, attr, None) def _gettext(lang): import builtins if lang == 'en': trans_en.install() else:
def __init__(self, database_url, **options): self._local = Local() self._database_url = database_url self._options = options self._engine = self.create_engine()
class Environment(Mapping): """ An environment wraps data for ORM records: - :attr:`cr`, the current database cursor; - :attr:`uid`, the current user id; - :attr:`context`, the current context dictionary; - :attr:`su`, whether in superuser mode. It provides access to the registry by implementing a mapping from model names to new api models. It also holds a cache for records, and a data structure to manage recomputations. """ _local = Local() @classproperty def envs(cls): return cls._local.environments @classmethod @contextmanager def manage(cls): """ Context manager for a set of environments. """ if hasattr(cls._local, 'environments'): yield else: try: cls._local.environments = Environments() yield finally: release_local(cls._local) @classmethod def reset(cls): """ Clear the set of environments. This may be useful when recreating a registry inside a transaction. """ cls._local.environments = Environments() def __new__(cls, cr, uid, context, su=False): if uid == SUPERUSER_ID: su = True assert context is not None args = (cr, uid, context, su) # if env already exists, return it env, envs = None, cls.envs for env in envs: if env.args == args: return env # otherwise create environment, and add it in the set self = object.__new__(cls) args = (cr, uid, frozendict(context), su) self.cr, self.uid, self.context, self.su = self.args = args self.registry = Registry(cr.dbname) self.cache = envs.cache self._cache_key = (cr, uid, su) self._protected = StackMap() # {field: ids, ...} self.all = envs envs.add(self) return self # # Mapping methods # def __contains__(self, model_name): """ Test whether the given model exists. """ return model_name in self.registry def __getitem__(self, model_name): """ Return an empty recordset from the given model. """ return self.registry[model_name]._browse(self, (), ()) def __iter__(self): """ Return an iterator on model names. """ return iter(self.registry) def __len__(self): """ Return the size of the model registry. """ return len(self.registry) def __eq__(self, other): return self is other def __ne__(self, other): return self is not other def __hash__(self): return object.__hash__(self) def __call__(self, cr=None, user=None, context=None, su=None): """ Return an environment based on ``self`` with modified parameters. :param cr: optional database cursor to change the current cursor :param user: optional user/user id to change the current user :param context: optional context dictionary to change the current context :param su: optional boolean to change the superuser mode """ cr = self.cr if cr is None else cr uid = self.uid if user is None else int(user) context = self.context if context is None else context su = (user is None and self.su) if su is None else su return Environment(cr, uid, context, su) def ref(self, xml_id, raise_if_not_found=True): """ return the record corresponding to the given ``xml_id`` """ return self['ir.model.data'].xmlid_to_object( xml_id, raise_if_not_found=raise_if_not_found) def is_superuser(self): """ Return whether the environment is in superuser mode. """ return self.su def is_admin(self): """ Return whether the current user has group "Access Rights", or is in superuser mode. """ return self.su or self.user._is_admin() def is_system(self): """ Return whether the current user has group "Settings", or is in superuser mode. """ return self.su or self.user._is_system() @property def user(self): """ return the current user (as an instance) """ return self(su=True)['res.users'].browse(self.uid) @property def company(self): """ return the company in which the user is logged in (as an instance) """ try: company_id = int(self.context.get('allowed_company_ids')[0]) if company_id in self.user.company_ids.ids: return self['res.company'].browse(company_id) return self.user.company_id except Exception: return self.user.company_id @property def companies(self): """ return a recordset of the enabled companies by the user """ try: # In case the user tries to bidouille the url (eg: cids=1,foo,bar) allowed_company_ids = self.context.get('allowed_company_ids') # Prevent the user to enable companies for which he doesn't have any access users_company_ids = self.user.company_ids.ids allowed_company_ids = [ company_id for company_id in allowed_company_ids if company_id in users_company_ids ] except Exception: # By setting the default companies to all user companies instead of the main one # we save a lot of potential trouble in all "out of context" calls, such as # /mail/redirect or /web/image, etc. And it is not unsafe because the user does # have access to these other companies. The risk of exposing foreign records # (wrt to the context) is low because all normal RPCs will have a proper # allowed_company_ids. # Examples: # - when printing a report for several records from several companies # - when accessing to a record from the notification email template # - when loading an binary image on a template allowed_company_ids = self.user.company_ids.ids return self['res.company'].browse(allowed_company_ids) @property def lang(self): """ return the current language code """ return self.context.get('lang') @contextmanager def do_in_draft(self): """ Context-switch to draft mode, where all field updates are done in cache only. """ if self.all.in_draft: yield else: try: self.all.in_draft = True yield finally: self.all.in_draft = False @property def in_draft(self): """ Return whether we are in draft mode. """ return self.all.in_draft def clear(self): """ Clear all record caches, and discard all fields to recompute. This may be useful when recovering from a failed ORM operation. """ self.cache.invalidate() self.all.todo.clear() @contextmanager def clear_upon_failure(self): """ Context manager that clears the environments (caches and fields to recompute) upon exception. """ try: yield except Exception: self.clear() raise def protected(self, field): """ Return the recordset for which ``field`` should not be invalidated or recomputed. """ return self[field.model_name].browse(self._protected.get(field, ())) @contextmanager def protecting(self, what, records=None): """ Prevent the invalidation or recomputation of fields on records. The parameters are either: - ``what`` a collection of fields and ``records`` a recordset, or - ``what`` a collection of pairs ``(fields, records)``. """ protected = self._protected try: protected.pushmap() what = what if records is None else [(what, records)] for fields, records in what: for field in fields: ids = protected.get(field, frozenset()) protected[field] = ids.union(records._ids) yield finally: protected.popmap() def field_todo(self, field): """ Return a recordset with all records to recompute for ``field``. """ ids = { rid for recs in self.all.todo.get(field, ()) for rid in recs.ids } return self[field.model_name].browse(ids) def check_todo(self, field, record): """ Check whether ``field`` must be recomputed on ``record``, and if so, return the corresponding recordset to recompute. """ for recs in self.all.todo.get(field, []): if recs & record: return recs def add_todo(self, field, records): """ Mark ``field`` to be recomputed on ``records``. """ recs_list = self.all.todo.setdefault(field, []) for i, recs in enumerate(recs_list): if recs.env == records.env: # only add records if not already in the recordset, much much # cheaper in case recs is big and records is a singleton # already present if not records <= recs: recs_list[i] |= records break else: recs_list.append(records) def remove_todo(self, field, records): """ Mark ``field`` as recomputed on ``records``. """ recs_list = [recs - records for recs in self.all.todo.pop(field, [])] recs_list = [r for r in recs_list if r] if recs_list: self.all.todo[field] = recs_list def has_todo(self): """ Return whether some fields must be recomputed. """ return bool(self.all.todo) def get_todo(self): """ Return a pair ``(field, records)`` to recompute. The field is such that none of its dependencies must be recomputed. """ field = min(self.all.todo, key=self.registry.field_sequence) return field, self.all.todo[field][0] @property def recompute(self): return self.all.recompute @contextmanager def norecompute(self): tmp = self.all.recompute self.all.recompute = False try: yield finally: self.all.recompute = tmp def cache_key(self, field): """ Return the key to store the value of ``field`` in cache, the full cache key being ``(key, field, record.id)``. """ return self if field.context_dependent else self._cache_key
from builtins import * # noqa try: from collections.abc import Mapping except ImportError: # < py3.3 from collections import Mapping from collections import OrderedDict from contextlib import contextmanager from werkzeug.local import Local, release_local from werkzeug.wsgi import ClosingIterator # a per request/job context. Generally, this will be the equivalent of thread # local storage, but if greenlets are being used it will be a greenlet local. context = Local() def clear(): release_local(context) def wsgi_middleware(app): def middleware(environ, start_response): return ClosingIterator(app(environ, start_response), clear) return middleware class ContextStack(Mapping): """A stacked set of dicts stored in a context.
import functools import logging import time from contextlib import contextmanager from typing import Optional import requests from bs4 import BeautifulSoup from requests import Response from requests.exceptions import RequestException from werkzeug.local import Local, release_local from werkzeug.urls import url_parse, url_fix from .__version__ import __version__ LOCAL_CONTEXT = Local() logger = logging.getLogger(__name__) bs4_parser = 'html.parser' default_timeout = (10.05, 30) def get_session(): """ Returns the Requests Session for the current local context. Creates a Session with default values if none exists. :return: Requests Session """
class Environment(Mapping): """ An environment wraps data for ORM records: - :attr:`cr`, the current database cursor; - :attr:`uid`, the current user id; - :attr:`context`, the current context dictionary; - :attr:`su`, whether in superuser mode. It provides access to the registry by implementing a mapping from model names to new api models. It also holds a cache for records, and a data structure to manage recomputations. """ _local = Local() @classproperty def envs(cls): return getattr(cls._local, 'environments', ()) @classmethod @contextmanager def manage(cls): """ Context manager for a set of environments. """ if hasattr(cls._local, 'environments'): yield else: try: cls._local.environments = Environments() yield finally: release_local(cls._local) @classmethod def reset(cls): """ Clear the set of environments. This may be useful when recreating a registry inside a transaction. """ cls._local.environments = Environments() def __new__(cls, cr, uid, context, su=False): if uid == SUPERUSER_ID: su = True assert context is not None args = (cr, uid, context, su) # if env already exists, return it env, envs = None, cls.envs for env in envs: if env.args == args: return env # otherwise create environment, and add it in the set self = object.__new__(cls) args = (cr, uid, frozendict(context), su) self.cr, self.uid, self.context, self.su = self.args = args self.registry = Registry(cr.dbname) self.cache = envs.cache self._protected = envs.protected # proxy to shared data structure self.all = envs envs.add(self) return self # # Mapping methods # def __contains__(self, model_name): """ Test whether the given model exists. """ return model_name in self.registry def __getitem__(self, model_name): """ Return an empty recordset from the given model. """ return self.registry[model_name]._browse(self, (), ()) def __iter__(self): """ Return an iterator on model names. """ return iter(self.registry) def __len__(self): """ Return the size of the model registry. """ return len(self.registry) def __eq__(self, other): return self is other def __ne__(self, other): return self is not other def __hash__(self): return object.__hash__(self) def __call__(self, cr=None, user=None, context=None, su=None): """ Return an environment based on ``self`` with modified parameters. :param cr: optional database cursor to change the current cursor :param user: optional user/user id to change the current user :param context: optional context dictionary to change the current context :param su: optional boolean to change the superuser mode """ cr = self.cr if cr is None else cr uid = self.uid if user is None else int(user) context = self.context if context is None else context su = (user is None and self.su) if su is None else su return Environment(cr, uid, context, su) def ref(self, xml_id, raise_if_not_found=True): """ return the record corresponding to the given ``xml_id`` """ return self['ir.model.data'].xmlid_to_object(xml_id, raise_if_not_found=raise_if_not_found) def is_superuser(self): """ Return whether the environment is in superuser mode. """ return self.su def is_admin(self): """ Return whether the current user has group "Access Rights", or is in superuser mode. """ return self.su or self.user._is_admin() def is_system(self): """ Return whether the current user has group "Settings", or is in superuser mode. """ return self.su or self.user._is_system() @property def user(self): """ return the current user (as an instance) """ return self(su=True)['res.users'].browse(self.uid) @property def company(self): """ return the company in which the user is logged in (as an instance) """ try: company_id = int(self.context.get('allowed_company_ids')[0]) if company_id in self.user.company_ids.ids: return self['res.company'].browse(company_id) return self.user.company_id except Exception: return self.user.company_id @property def companies(self): """ return a recordset of the enabled companies by the user """ try: # In case the user tries to bidouille the url (eg: cids=1,foo,bar) allowed_company_ids = self.context.get('allowed_company_ids') # Prevent the user to enable companies for which he doesn't have any access users_company_ids = self.user.company_ids.ids allowed_company_ids = [company_id for company_id in allowed_company_ids if company_id in users_company_ids] except Exception: # By setting the default companies to all user companies instead of the main one # we save a lot of potential trouble in all "out of context" calls, such as # /mail/redirect or /web/image, etc. And it is not unsafe because the user does # have access to these other companies. The risk of exposing foreign records # (wrt to the context) is low because all normal RPCs will have a proper # allowed_company_ids. # Examples: # - when printing a report for several records from several companies # - when accessing to a record from the notification email template # - when loading an binary image on a template allowed_company_ids = self.user.company_ids.ids return self['res.company'].browse(allowed_company_ids) @property def lang(self): """ return the current language code """ return self.context.get('lang') def clear(self): """ Clear all record caches, and discard all fields to recompute. This may be useful when recovering from a failed ORM operation. """ self.cache.invalidate() self.all.tocompute.clear() self.all.towrite.clear() @contextmanager def clear_upon_failure(self): """ Context manager that clears the environments (caches and fields to recompute) upon exception. """ tocompute = { field: set(ids) for field, ids in self.all.tocompute.items() } towrite = { model: { record_id: dict(values) for record_id, values in id_values.items() } for model, id_values in self.all.towrite.items() } try: yield except Exception: self.clear() self.all.tocompute.update(tocompute) for model, id_values in towrite.items(): for record_id, values in id_values.items(): self.all.towrite[model][record_id].update(values) raise def is_protected(self, field, record): """ Return whether `record` is protected against invalidation or recomputation for `field`. """ return record.id in self._protected.get(field, ()) def protected(self, field): """ Return the recordset for which ``field`` should not be invalidated or recomputed. """ return self[field.model_name].browse(self._protected.get(field, ())) @contextmanager def protecting(self, what, records=None): """ Prevent the invalidation or recomputation of fields on records. The parameters are either: - ``what`` a collection of fields and ``records`` a recordset, or - ``what`` a collection of pairs ``(fields, records)``. """ protected = self._protected try: protected.pushmap() what = what if records is None else [(what, records)] for fields, records in what: for field in fields: ids = protected.get(field, frozenset()) protected[field] = ids.union(records._ids) yield finally: protected.popmap() def fields_to_compute(self): """ Return a view on the field to compute. """ return self.all.tocompute.keys() def records_to_compute(self, field): """ Return the records to compute for ``field``. """ ids = self.all.tocompute.get(field, ()) return self[field.model_name].browse(ids) def is_to_compute(self, field, record): """ Return whether ``field`` must be computed on ``record``. """ return record.id in self.all.tocompute.get(field, ()) def add_to_compute(self, field, records): """ Mark ``field`` to be computed on ``records``, return newly added records. """ if not records: return records ids = self.all.tocompute[field] added_ids = [id_ for id_ in records._ids if id_ not in ids] ids.update(added_ids) return records.browse(added_ids) def remove_to_compute(self, field, records): """ Mark ``field`` as computed on ``records``. """ if not records: return ids = self.all.tocompute.get(field, None) if ids is None: return ids.difference_update(records._ids) if not ids: del self.all.tocompute[field] @contextmanager def norecompute(self): """ Delay recomputations (deprecated: this is not the default behavior). """ yield