Package turbogears :: Module util

Source Code for Module turbogears.util

  1  """The TurboGears utility module.""" 
  2   
  3  import os 
  4  import sys 
  5  import re 
  6  import logging 
  7  import warnings 
  8  import htmlentitydefs 
  9  import socket 
 10  import struct 
 11  from inspect import getargspec, getargvalues 
 12  from itertools import izip, islice, chain 
 13  from operator import isSequenceType 
 14  from Cookie import _quote as quote_cookie, _unquote as unquote_cookie 
 15   
 16  import pkg_resources 
 17   
 18  from cherrypy import request 
 19   
 20  from turbogears.decorator import decorator 
 21  from turbogears import config 
22 23 24 -def deprecated(message=None):
25 """Decorator which can be used to mark functions as deprecated. 26 27 It will result in a warning being emitted when the function is used. 28 29 Inspired by http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/391367 30 31 """ 32 def decorate(func): 33 if not decorate.message: 34 decorate.message = ("Call to deprecated function %s." 35 % func.__name__) 36 def new_func(*args, **kwargs): 37 if not decorate.warned: 38 warnings.warn(decorate.message, category=DeprecationWarning, 39 stacklevel=2) 40 decorate.warned = True 41 return func(*args, **kwargs)
42 new_func.__name__ = func.__name__ 43 new_func.__doc__ = func.__doc__ 44 new_func.__dict__.update(func.__dict__) 45 return new_func 46 decorate.message = message 47 decorate.warned = False 48 return decorate 49
50 -def missing_dependency_error(name=None):
51 msg = """\ 52 Before you can run this command, you need to install all the project's 53 dependencies by running "python setup.py develop" in the project directory, or 54 you can install the application with "python setup.py install", or build an egg 55 with "python setup.py bdist_egg" and install it with "easy_install dist/<egg>". 56 57 If you are stuck, visit http://docs.turbogears.org/GettingHelp for support.""" 58 if name: 59 msg = ("This project requires the %s package but it could not be " 60 "found.\n\n" % name) + msg 61 return msg
62
63 -class Bunch(dict):
64 """Simple but handy collector of a bunch of named stuff.""" 65
66 - def __repr__(self):
67 keys = self.keys() 68 keys.sort() 69 args = ', '.join(['%s=%r' % (key, self[key]) for key in keys]) 70 return '%s(%s)' % (self.__class__.__name__, args)
71
72 - def __getattr__(self, name):
73 try: 74 return self[name] 75 except KeyError: 76 raise AttributeError(name)
77 78 __setattr__ = dict.__setitem__ 79
80 - def __delattr__(self, name):
81 try: 82 del self[name] 83 except KeyError: 84 raise AttributeError(name)
85
86 87 -class DictObj(Bunch):
88 89 @deprecated("Use Bunch instead of DictObj and DictWrapper.")
90 - def __init__(self, *args, **kw):
91 super(DictObj, self).__init__(*args, **kw)
92 93 DictWrapper = DictObj
94 95 96 -def Enum(*names):
97 """True immutable symbolic enumeration with qualified value access. 98 99 Written by Zoran Isailovski: 100 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/413486 101 102 """ 103 104 # Uncomment the following line if you don't like empty enums. 105 # assert names, "Empty enums are not supported" 106 107 class EnumClass(object): 108 __slots__ = names 109 def __iter__(self): 110 return iter(constants)
111 def __len__(self): 112 return len(constants) 113 def __getitem__(self, i): 114 return constants[i] 115 def __repr__(self): 116 return 'Enum' + str(names) 117 def __str__(self): 118 return 'enum ' + str(constants) 119 120 enumType = EnumClass() 121 122 class EnumValue(object): 123 __slots__ = ('__value') 124 def __init__(self, value): 125 self.__value = value 126 Value = property(lambda self: self.__value) 127 EnumType = property(lambda self: enumType) 128 def __hash__(self): 129 return hash(self.__value) 130 def __cmp__(self, other): 131 # C fans might want to remove the following assertion 132 # to make all enums comparable by ordinal value {;)) 133 assert self.EnumType is other.EnumType, \ 134 "Only values from the same enum are comparable" 135 return cmp(self.__value, other.__value) 136 def __invert__(self): 137 return constants[maximum - self.__value] 138 def __nonzero__(self): 139 return bool(self.__value) 140 def __repr__(self): 141 return str(names[self.__value]) 142 143 maximum = len(names) - 1 144 constants = [None] * len(names) 145 for i, each in enumerate(names): 146 val = EnumValue(i) 147 setattr(EnumClass, each, val) 148 constants[i] = val 149 constants = tuple(constants) 150 return enumType 151
152 153 -class setlike(list):
154 """Set preserving item order.""" 155
156 - def add(self, item):
157 if item not in self: 158 self.append(item)
159
160 - def add_all(self, iterable):
161 for item in iterable: 162 self.add(item)
163
164 165 -def get_project_meta(name):
166 """Get egg-info file with that name in the current project.""" 167 for dirname in os.listdir("./"): 168 if dirname.lower().endswith("egg-info"): 169 fname = os.path.join(dirname, name) 170 return fname
171
172 173 -def get_project_config():
174 """Try to select appropriate project configuration file.""" 175 return os.path.exists('setup.py') and 'dev.cfg' or 'prod.cfg'
176
177 178 -def load_project_config(configfile=None):
179 """Try to update the project settings from the config file specified. 180 181 If configfile is C{None}, uses L{get_project_config} to locate one. 182 183 """ 184 if configfile is None: 185 configfile = get_project_config() 186 if not os.path.isfile(configfile): 187 print 'Config file %s not found or is not a file.' % ( 188 os.path.abspath(configfile),) 189 sys.exit() 190 package = get_package_name() 191 config.update_config(configfile=configfile, 192 modulename = package + ".config")
193
194 195 -def get_package_name():
196 """Try to find out the package name of the current directory.""" 197 package = config.get("package") 198 if package: 199 return package 200 if hasattr(sys, 'argv') and "--egg" in sys.argv: 201 projectname = sys.argv[sys.argv.index("--egg")+1] 202 egg = pkg_resources.get_distribution(projectname) 203 top_level = egg._get_metadata("top_level.txt") 204 else: 205 fname = get_project_meta('top_level.txt') 206 top_level = fname and open(fname) or [] 207 for package in top_level: 208 package = package.rstrip() 209 if package and package != 'locales': 210 return package
211
212 213 -def get_project_name():
214 pkg_info = get_project_meta('PKG-INFO') 215 if pkg_info: 216 name = list(open(pkg_info))[1][6:-1] 217 return name.strip()
218
219 220 -def get_model():
221 package_name = get_package_name() 222 if not package_name: 223 return None 224 package = __import__(package_name, {}, {}, ["model"]) 225 if hasattr(package, "model"): 226 return package.model
227
228 229 -def ensure_sequence(obj):
230 """Construct a sequence from object.""" 231 if obj is None: 232 return [] 233 elif isSequenceType(obj): 234 return obj 235 else: 236 return [obj]
237
238 239 -def to_kw(func, args, kw, start=0):
240 """Convert all applicable arguments to keyword arguments.""" 241 argnames, defaults = getargspec(func)[::3] 242 defaults = ensure_sequence(defaults) 243 kv_pairs = izip( 244 islice(argnames, start, len(argnames) - len(defaults)), args) 245 for k, v in kv_pairs: 246 kw[k] = v 247 return args[len(argnames)-len(defaults)-start:], kw
248
249 250 -def from_kw(func, args, kw, start=0):
251 """Extract named positional arguments from keyword arguments.""" 252 argnames, defaults = getargspec(func)[::3] 253 defaults = ensure_sequence(defaults) 254 newargs = [kw.pop(name) for name in islice(argnames, start, 255 len(argnames) - len(defaults)) if name in kw] 256 newargs.extend(args) 257 return newargs, kw
258
259 260 -def adapt_call(func, args, kw, start=0):
261 """Remove unsupported func arguments from given args list and kw dict. 262 263 @param func: the callable to inspect for supported arguments 264 @type func: callable 265 266 @param args: the names of the positional arguments intended to be passed 267 to func 268 @type args: list 269 270 @param kw: the keyword arguments intended to be passed to func 271 @type kw: dict 272 273 @keyparam start: the number of items from the start of the argument list of 274 func to disregard. Set start=1 to use adapt_call on a bound method to 275 disregard the implicit self argument. 276 277 @type start: int 278 279 Returns args list and kw dict from which arguments unsupported by func 280 have been removed. The passed in kw dict is also stripped as a side-effect. 281 The returned objects can then be used to call the target function. 282 283 Example: 284 285 def myfunc(arg1, arg2, kwarg1='foo'): 286 pass 287 288 args, kw = adapt_call(myfunc, ['args1, 'bogus1'], 289 {'kwargs1': 'bar', 'bogus2': 'spamm'}) 290 # --> ['args1'], {'kwargs1': 'bar'} 291 myfunc(*args, **kw) 292 293 """ 294 argnames, varargs, kwargs = getargspec(func)[:3] 295 del argnames[:start] 296 if kwargs in (None, "_decorator__kwargs"): 297 remove_keys(kw, [key for key in kw if key not in argnames]) 298 if varargs in (None, "_decorator__varargs"): 299 args = args[:len(argnames)] 300 for n, key in enumerate(argnames): 301 if key in kw: 302 args = args[:n] 303 break 304 return args, kw
305
306 307 -def call_on_stack(func_name, kw, start=0):
308 """Check if a call to function matching pattern is on stack.""" 309 try: 310 frame = sys._getframe(start+1) 311 except ValueError: 312 return False 313 while frame.f_back: 314 frame = frame.f_back 315 if frame.f_code.co_name == func_name: 316 args = getargvalues(frame)[3] 317 for key in kw.iterkeys(): 318 try: 319 if kw[key] != args[key]: 320 break 321 except (KeyError, TypeError): 322 break 323 else: 324 return True 325 return False
326
327 328 -def remove_keys(dict_, seq):
329 """Gracefully remove keys from dict.""" 330 for key in seq: 331 dict_.pop(key, None) 332 return dict_
333
334 335 -def has_arg(func, argname):
336 """Check whether function has argument.""" 337 return argname in getargspec(func)[0]
338
339 340 -def arg_index(func, argname):
341 """Find index of argument as declared for given function.""" 342 argnames = getargspec(func)[0] 343 if has_arg(func, argname): 344 return argnames.index(argname) 345 else: 346 return None
347
348 349 -def inject_arg(func, argname, argval, args, kw, start=0):
350 """Insert argument into call.""" 351 argnames, defaults = getargspec(func)[::3] 352 defaults = ensure_sequence(defaults) 353 pos = arg_index(func, argname) 354 if pos is None or pos > len(argnames) - len(defaults) - 1: 355 kw[argname] = argval 356 else: 357 pos -= start 358 args = tuple(chain(islice(args, pos), (argval,), 359 islice(args, pos, None))) 360 return args, kw
361
362 363 -def inject_args(func, injections, args, kw, start=0):
364 """Insert arguments into call.""" 365 for argname, argval in injections.iteritems(): 366 args, kw = inject_arg(func, argname, argval, args, kw, start) 367 return args, kw
368
369 370 -def inject_call(func, injections, *args, **kw):
371 """Insert arguments and call.""" 372 args, kw = inject_args(func, injections, args, kw) 373 return func(*args, **kw)
374
375 376 -def add_tg_args(func, args):
377 """Add hint for special arguments that shall not be removed.""" 378 try: 379 tg_args = func._tg_args 380 except AttributeError: 381 tg_args = set() 382 tg_args.update(args) 383 func._tg_args = tg_args
384
385 386 -def bind_args(**add):
387 """Call with arguments set to a predefined value.""" 388 def entagle(func): 389 return lambda func, *args, **kw: inject_call(func, add, *args, **kw)
390 391 def make_decorator(func): 392 argnames, varargs, kwargs, defaults = getargspec(func) 393 defaults = list(ensure_sequence(defaults)) 394 defaults = [d for d in defaults if 395 argnames[-len(defaults) + defaults.index(d)] not in add] 396 argnames = [arg for arg in argnames if arg not in add] 397 return decorator(entagle, (argnames, varargs, kwargs, defaults))(func) 398 399 return make_decorator 400
401 402 -def recursive_update(to_dict, from_dict):
403 """Recursively update all dicts in to_dict with values from from_dict.""" 404 # probably slow as hell :( should be optimized somehow... 405 for k, v in from_dict.iteritems(): 406 if isinstance(v, dict) and isinstance(to_dict[k], dict): 407 recursive_update(to_dict[k], v) 408 else: 409 to_dict[k] = v 410 return to_dict
411
412 413 -def combine_contexts(frames=None, depth=None):
414 """Combine contexts (globals, locals) of frames.""" 415 locals_ = {} 416 globals_ = {} 417 if frames is None: 418 frames = [] 419 if depth is not None: 420 frames.extend([sys._getframe(d+1) for d in depth]) 421 for frame in frames: 422 locals_.update(frame.f_locals) 423 globals_.update(frame.f_globals) 424 return locals_, globals_
425
426 427 -def request_available():
428 """Check if cherrypy.request is available.""" 429 try: 430 setattr(request, "tg_dumb_attribute", True) 431 return True 432 except AttributeError: 433 return False
434
435 436 -def flatten_sequence(seq):
437 """Flatten sequence.""" 438 for item in seq: 439 if isSequenceType(item) and not isinstance(item, basestring): 440 for item in flatten_sequence(item): 441 yield item 442 else: 443 yield item
444
445 446 -def pop_from_nested_dict(params, keys):
447 """Pop a value from a nested parameter dictionary. 448 449 You must pass a list or dotted key as the key chain to the value. 450 If a value for this key chain is found, it will be removed and 451 the value will be returned. If this creates empty subkeys, 452 these will also be removed. If no single value is associated 453 with the key chain, then None will be returned. 454 455 """ 456 if not isinstance(keys, list): 457 keys = keys.split('.') 458 values = [] 459 for key in keys: 460 if not isinstance(params, dict) or key not in params: 461 return None 462 values.append(params) 463 params = params[key] 464 if isinstance(params, dict): 465 if None in params: 466 value = params.pop(None) 467 if params: 468 return value 469 else: 470 return None 471 else: 472 value = params 473 for key, params in reversed(zip(keys, values)): 474 del params[key] 475 if params: 476 break 477 return value
478
479 480 -def pop_request_params(keys):
481 """Pop a value from the nested request parameter dictionary.""" 482 return pop_from_nested_dict(request.params, keys)
483
484 485 -def load_class(dottedpath):
486 """Load a class from a module in dotted-path notation. 487 488 E.g.: load_class("package.module.class"). 489 490 Based on recipe 16.3 from Python Cookbook, 2ed., by Alex Martelli, 491 Anna Martelli Ravenscroft, and David Ascher (O'Reilly Media, 2005) 492 493 """ 494 assert dottedpath is not None, "dottedpath must not be None" 495 splitted_path = dottedpath.split('.') 496 modulename = '.'.join(splitted_path[:-1]) 497 classname = splitted_path[-1] 498 try: 499 try: 500 module = __import__(modulename, globals(), locals(), [classname]) 501 except ValueError: # Py < 2.5 502 if not modulename: 503 module = __import__(__name__.split('.')[0], 504 globals(), locals(), [classname]) 505 except ImportError: 506 # properly log the exception information and return None 507 # to tell caller we did not succeed 508 logging.exception('tg.utils: Could not import %s' 509 ' because an exception occurred', dottedpath) 510 return None 511 try: 512 return getattr(module, classname) 513 except AttributeError: 514 logging.exception('tg.utils: Could not import %s' 515 ' because the class was not found', dottedpath) 516 return None
517
518 519 -def parse_http_accept_header(accept):
520 """Parse an HTTP Accept header (RFC 2616) into a sorted list. 521 522 The quality factors in the header determine the sort order. 523 The values can include possible media-range parameters. 524 This function can also be used for the Accept-Charset, 525 Accept-Encoding and Accept-Language headers. 526 527 """ 528 if accept is None: 529 return [] 530 items = [] 531 for item in accept.split(','): 532 params = item.split(';') 533 for i, param in enumerate(params[1:]): 534 param = param.split('=', 1) 535 if param[0].strip() == 'q': 536 try: 537 q = float(param[1]) 538 if not 0 < q <= 1: 539 raise ValueError 540 except (IndexError, ValueError): 541 q = 0 542 else: 543 item = ';'.join(params[:i+1]) 544 break 545 else: 546 q = 1 547 if q: 548 item = item.strip() 549 if item: 550 items.append((item, q)) 551 items.sort(key=lambda item: -item[1]) 552 return [item[0] for item in items]
553
554 555 -def simplify_http_accept_header(accept):
556 """Parse an HTTP Accept header (RFC 2616) into a preferred value. 557 558 The quality factors in the header determine the preference. 559 Possible media-range parameters are allowed, but will be ignored. 560 This function can also be used for the Accept-Charset, 561 Accept-Encoding and Accept-Language headers. 562 563 This is similar to parse_http_accept_header(accept)[0], but faster. 564 565 """ 566 if accept is None: 567 return None 568 best_item = accept 569 best_q = 0 570 for item in accept.split(','): 571 params = item.split(';') 572 item = params.pop(0) 573 for param in params: 574 param = param.split('=', 1) 575 if param[0].strip() == 'q': 576 try: 577 q = float(param[1]) 578 if not 0 < q <= 1: 579 raise ValueError 580 except (IndexError, ValueError): 581 q = 0 582 break 583 else: 584 q = 1 585 if q > best_q: 586 item = item.strip() 587 if item: 588 best_item = item 589 if q == 1: 590 break 591 best_q = q 592 return best_item
593
594 595 -def to_unicode(value):
596 """Convert encoded string to unicode string. 597 598 Uses get_template_encoding_default() to guess source string encoding. 599 Handles turbogears.i18n.lazystring correctly. 600 601 """ 602 if isinstance(value, str): 603 # try to make sure we won't get UnicodeDecodeError from the template 604 # by converting all encoded strings to Unicode strings 605 try: 606 value = unicode(value) 607 except UnicodeDecodeError: 608 try: 609 value = unicode(value, get_template_encoding_default()) 610 except UnicodeDecodeError: 611 # fail early 612 raise ValueError("Non-unicode string: %r" % value) 613 return value
614
615 616 -def to_utf8(value):
617 """Convert a unicode string to utf-8 encoded plain string. 618 619 Handles turbogears.i18n.lazystring correctly. 620 621 Does nothing to already encoded string. 622 623 """ 624 if isinstance(value, str): 625 pass 626 elif hasattr(value, '__unicode__'): 627 value = unicode(value) 628 if isinstance(value, unicode): 629 value = value.encode('utf-8') 630 return value
631
632 633 -def get_template_encoding_default(engine_name=None):
634 """Return default encoding for template files (Kid, Genshi, etc.).""" 635 if engine_name is None: 636 engine_name = config.get('tg.defaultview', 'genshi') 637 return config.get('%s.encoding' % engine_name, 638 config.get('%s.default_encoding' % engine_name, 'utf-8'))
639 640 641 _format_mime_types = dict( 642 plain='text/plain', text='text/plain', 643 html='text/html', xhtml = 'text/html', # see note below 644 xml='text/xml', json='application/json')
645 646 -def get_mime_type_for_format(format):
647 """Return default MIME media type for a template format. 648 649 Note: By default we are serving xhtml as "text/html" instead of the more 650 correct "application/xhtml+xml", since many browsers, particularly MSIE, 651 do not support this. We are assuming that xhtml means XHTML 1.0 here, 652 where this approach is possible. It would be possible to use some kind 653 of content negotiation to deliver a customized content type, but we avoid 654 this because it causes more harm (e.g. with proxies involved) than good. 655 656 If you want to serve the proper content type (e.g. for XHTML 1.1), 657 set tg.format_mime_types= {'xhtml': 'application/xhtml+xml'}. 658 You can also set a particular content type per controller using the 659 content_type parameter of the expose decorator. 660 661 For detailed information about this issues, see here: 662 http://www.smackthemouse.com/xhtmlxml, http://schneegans.de/web/xhtml/. 663 664 """ 665 mime_type = config.get('tg.format_mime_types', {}).get(format) 666 if not mime_type: 667 mime_type = _format_mime_types.get(format, 'text/html') 668 return mime_type
669
670 671 -def mime_type_has_charset(mime_type):
672 """Return whether the MIME media type supports a charset parameter. 673 674 Note: According to RFC4627, we do not output a charset parameter 675 for "application/json" (this type always uses a UTF encoding). 676 677 """ 678 if not mime_type: 679 return False 680 if mime_type.startswith('text/'): 681 return True 682 if mime_type.startswith('application/'): 683 if mime_type.endswith('/xml') or mime_type.endswith('+xml'): 684 return True 685 if mime_type.endswith('/javascript'): 686 return True 687 return False
688
689 690 -def find_precision(value):
691 """Find precision of some arbitrary value. 692 693 The main intention for this function is to use it together with 694 turbogears.i18n.format.format_decimal() where one has to inform 695 the precision wanted. So, use it like this: 696 697 format_decimal(some_number, find_precision(some_number)) 698 699 """ 700 decimals = '' 701 try: 702 decimals = str(value).split('.', 1)[1] 703 except IndexError: 704 pass 705 return len(decimals)
706
707 708 -def copy_if_mutable(value, feedback=False):
709 """Make a copy of the value if it is mutable. 710 711 Returns the value. If feedback is set to true, also returns 712 whether value was mutable as the second element of a tuple. 713 714 """ 715 if isinstance(value, dict): 716 mutable = True 717 value = value.copy() 718 elif isinstance(value, list): 719 mutable = True 720 value = value[:] 721 else: 722 mutable = False 723 if feedback: 724 return value, mutable 725 else: 726 return value
727
728 729 -def fixentities(htmltext):
730 """Replace HTML character entities with numerical references. 731 732 Note: This won't handle CDATA sections properly. 733 734 """ 735 def repl(matchobj): 736 entity = htmlentitydefs.entitydefs.get(matchobj.group(1).lower()) 737 if not entity: 738 return matchobj.group(0) 739 elif len(entity) == 1: 740 if entity in "&<>'\"": 741 return matchobj.group(0) 742 return "&#%d;" % ord(entity) 743 else: 744 return entity
745 return re.sub("&(\w+);?", repl, htmltext) 746 747 748 if hasattr(socket, 'inet_pton') and hasattr(socket, 'AF_INET6'):
749 750 - def inet6_aton(addr):
751 """Convert IP6 standard hex notation to IP6 address.""" 752 return socket.inet_pton(socket.AF_INET6, addr)
753 754 else: # Windows etc. 755 756 import string 757 _inet6_chars = string.hexdigits + ':.'
758 759 - def inet6_aton(addr):
760 """Convert IPv6 standard hex notation to IPv6 address. 761 762 Inspired by http://twistedmatrix.com/trac/. 763 764 """ 765 faulty = addr.lstrip(_inet6_chars) 766 if faulty: 767 raise ValueError("Illegal character '%c' in IPv6 address" % faulty[0]) 768 parts = addr.split(':') 769 elided = parts.count('') 770 extenso = '.' in parts[-1] and 7 or 8 771 if len(parts) > extenso or elided > 3: 772 raise ValueError("Syntactically invalid IPv6 address") 773 if elided == 3: 774 return '\x00' * 16 775 if elided: 776 zeros = ['0'] * (extenso - len(parts) + elided) 777 if addr.startswith('::'): 778 parts[:2] = zeros 779 elif addr.endswith('::'): 780 parts[-2:] = zeros 781 else: 782 idx = parts.index('') 783 parts[idx:idx+1] = zeros 784 if len(parts) != extenso: 785 raise ValueError("Syntactically invalid IPv6 address") 786 if extenso == 7: 787 ipv4 = parts.pop() 788 if ipv4.count('.') != 3: 789 raise ValueError("Syntactically invalid IPv6 address") 790 parts = [int(x, 16) for x in parts] 791 return struct.pack('!6H', *parts) + socket.inet_aton(ipv4) 792 else: 793 parts = [int(x, 16) for x in parts] 794 return struct.pack('!8H', *parts)
795
796 797 -def inet_aton(addr):
798 """Convert IPv4 or IPv6 notation to IPv6 address.""" 799 if ':' in addr: 800 return inet6_aton(addr) 801 else: 802 return struct.pack('!QL', 0, 0xffff) + socket.inet_aton(addr)
803
804 805 -def _inet_prefix(addr, masked):
806 """Remove the number of masked bits from the IPV6 address.""" 807 hi, lo = struct.unpack("!QQ", addr) 808 return (hi << 64 | lo) >> masked
809
810 811 -def match_ip(cidr, ip):
812 """Check whether IP address matches CIDR IP address block.""" 813 if '/' in cidr: 814 cidr, prefix = cidr.split('/', 1) 815 masked = (':' in cidr and 128 or 32) - int(prefix) 816 else: 817 masked = None 818 cidr = inet_aton(cidr) 819 ip = inet_aton(ip) 820 if masked: 821 cidr = _inet_prefix(cidr, masked) 822 ip = _inet_prefix(ip, masked) 823 return ip == cidr
824 825 826 __all__ = ["Bunch", "DictObj", "DictWrapper", "Enum", "setlike", 827 "get_package_name", "get_model", "load_project_config", 828 "ensure_sequence", "has_arg", "to_kw", "from_kw", "adapt_call", 829 "call_on_stack", "remove_keys", "arg_index", 830 "inject_arg", "inject_args", "add_tg_args", "bind_args", 831 "recursive_update", "combine_contexts", "request_available", 832 "flatten_sequence", "pop_from_nested_dict", 833 "pop_request_params", "load_class", 834 "parse_http_accept_header", "simplify_http_accept_header", 835 "to_unicode", "to_utf8", "quote_cookie", "unquote_cookie", 836 "get_template_encoding_default", "get_mime_type_for_format", 837 "mime_type_has_charset", "find_precision", "copy_if_mutable", 838 "match_ip", "deprecated"] 839