59 return '/' + ref |
60 return '/' + ref |
60 return ref |
61 return ref |
61 |
62 |
62 @classmethod |
63 @classmethod |
63 def remove_begin_slash(cls, ref): |
64 def remove_begin_slash(cls, ref): |
64 if ref.startswith('/'): |
65 while ref.startswith('/'): |
65 return ref.replace('/', '', 1) |
66 ref = ref.replace('/', '', 1) |
66 return ref |
67 return ref |
67 |
68 |
68 @classmethod |
69 @classmethod |
69 def remove_end(self, path, str): |
70 def remove_end(self, path, str): |
70 try: |
71 try: |
71 (ret, sep, rest) = path.partition(str) |
72 (ret, sep, rest) = path.partition(str) |
72 return ret |
73 return ret |
191 |
194 |
192 @classmethod |
195 @classmethod |
193 def get_filename(cls, ref): |
196 def get_filename(cls, ref): |
194 """ |
197 """ |
195 get file name part from ref |
198 get file name part from ref |
196 1. get file extension |
199 1. get file name |
197 @return: a reference. E.g. (foo/test.confml) => confml |
200 @return: a reference. E.g. (foo/test.confml) => test.confml |
198 """ |
201 """ |
199 return ref.rsplit('/', 1)[-1] |
202 return ref.rsplit('/', 1)[-1] |
200 |
203 |
201 @classmethod |
204 @classmethod |
202 def get_path(cls, ref): |
205 def get_path(cls, ref): |
203 """ |
206 """ |
204 get file name part from ref |
207 get path part from ref |
205 1. get file extension |
208 1. get path from ref |
206 @return: a reference. E.g. (foo/test.confml) => confml |
209 @return: a reference. E.g. (foo/test.confml) => foo |
207 """ |
210 """ |
208 if len(ref.rsplit('/', 1)) == 2: |
211 if len(ref.rsplit('/', 1)) == 2: |
209 return ref.rsplit('/', 1)[0] |
212 return ref.rsplit('/', 1)[0] |
210 else: |
213 else: |
211 return "" |
214 return "" |
230 2. convert path delims to dots |
233 2. convert path delims to dots |
231 3. using double underscores for directory separation |
234 3. using double underscores for directory separation |
232 @return: a dotted reference. E.g. (foo/test.confml) => foo_test |
235 @return: a dotted reference. E.g. (foo/test.confml) => foo_test |
233 """ |
236 """ |
234 ref = ref.replace('/', '__') |
237 ref = ref.replace('/', '__') |
|
238 # Change the python comment character also as underscore so that the tokenizer |
|
239 # does not leave anything out |
|
240 ref = ref.replace('#', '_') |
235 newref = '' |
241 newref = '' |
236 first_token = True |
242 first_token = True |
237 try: |
243 try: |
238 for toknum, tokval, spos, epos, _ in tokenize.generate_tokens(StringIO.StringIO(unicode(ref)).readline): |
244 for toknum, tokval, spos, epos, _ in tokenize.generate_tokens(StringIO.StringIO(unicode(ref)).readline): |
239 if toknum == ENDMARKER: |
245 if toknum == ENDMARKER: |
376 elems.append(".*") |
392 elems.append(".*") |
377 else: |
393 else: |
378 elems.append(refelem.replace("*","[^\.]*")) |
394 elems.append(refelem.replace("*","[^\.]*")) |
379 return "\\.".join(elems)+"$" |
395 return "\\.".join(elems)+"$" |
380 |
396 |
|
397 @classmethod |
|
398 def has_wildcard(cls, ref): |
|
399 """ |
|
400 Tests if the ref has any wildcards '*' in it. |
|
401 @return: Boolean value. True when wildcards are found. |
|
402 """ |
|
403 return ref.find('*') != -1 |
|
404 |
|
405 @classmethod |
|
406 def get_static_ref(cls, ref): |
|
407 """ |
|
408 Checks if the ref has any wildcards and return the non wildcard part of ref. |
|
409 @return: string. |
|
410 """ |
|
411 retparts = [] |
|
412 for part in cls.split_ref(ref): |
|
413 if cls.has_wildcard(part): |
|
414 break |
|
415 else: |
|
416 retparts.append(part) |
|
417 return ".".join(retparts) |
|
418 |
381 def extract_delimited_tokens(string, delimiters=('${', '}')): |
419 def extract_delimited_tokens(string, delimiters=('${', '}')): |
382 """ |
420 """ |
383 Return a list of all tokens delimited by the given strings in the given string. |
421 Return a list of all tokens delimited by the given strings in the given string. |
384 This function returns basically the first row of the result of |
422 This function returns basically the first row of the result of |
385 extract_delimited_token_tuples(), with duplicates removed. |
423 extract_delimited_token_tuples(), with duplicates removed. |
386 |
424 |
387 >>> dottedref.extract_refs("test1 ${my.ref1} test2 ${ my.ref1 } ${my.ref2}") |
425 >>> dottedref.extract_refs("test1 ${my.ref1} test2 ${ my.ref1 } ${my.ref2}") |
388 ['my.ref1', 'my.ref2'] |
426 ['my.ref1', 'my.ref2'] |
389 """ |
427 """ |
390 ref_tuples = extract_delimited_token_tuples(string, delimiters) |
428 ref_tuples = extract_delimited_token_tuples(string, delimiters) |
391 return distinct_array([ref for ref, raw_ref in ref_tuples]) |
429 return distinct_array([u'%s' % ref for ref, raw_ref in ref_tuples]) |
392 |
430 |
393 def extract_delimited_token_tuples(string, delimiters=('${', '}')): |
431 def extract_delimited_token_tuples(string, delimiters=('${', '}')): |
394 """ |
432 """ |
395 Extract a list of (token, raw_token) tuples from the given string. |
433 Extract a list of (token, raw_token) tuples from the given string. |
396 'token' is the the token extracted from the string and trimmed (surrounding |
434 'token' is the the token extracted from the string and trimmed (surrounding |
441 for entry in tokens.itervalues(): |
479 for entry in tokens.itervalues(): |
442 for raw_token in entry.raw_tokens: |
480 for raw_token in entry.raw_tokens: |
443 result = result.replace(raw_token, entry.value) |
481 result = result.replace(raw_token, entry.value) |
444 return result |
482 return result |
445 |
483 |
446 def expand_refs_by_default_view(string, default_view, delimiters=('${', '}'), default_value_for_missing=''): |
484 def expand_refs_by_default_view(string, default_view, delimiters=('${', '}'), default_value_for_missing='', |
|
485 catch_not_found=True): |
447 """ |
486 """ |
448 Convenience function for expanding the refs in a string using setting values. |
487 Convenience function for expanding the refs in a string using setting values. |
449 @param default_value_for_missing: The default value used if a setting for |
488 @param default_value_for_missing: The default value used if a setting for |
450 a reference cannot be found. |
489 a reference cannot be found. Has no effect if catch_not_found is False. |
|
490 @param catch_not_found: If True, the NotFound exception raised when a setting |
|
491 is not found is caught and the value of default_value_for_missing is inserted |
|
492 in its place. |
451 @return: The expanded string. |
493 @return: The expanded string. |
452 """ |
494 """ |
453 def expand(ref, index): |
495 def expand(ref, index): |
454 try: |
496 if catch_not_found: |
|
497 try: |
|
498 return default_view.get_feature(ref).get_original_value() |
|
499 except exceptions.NotFound: |
|
500 logging.getLogger('cone').error("Feature '%s' not found" % ref) |
|
501 return default_value_for_missing |
|
502 else: |
455 return default_view.get_feature(ref).get_original_value() |
503 return default_view.get_feature(ref).get_original_value() |
456 except exceptions.NotFound: |
|
457 logging.getLogger('cone').error("Feature '%s' not found" % ref) |
|
458 return default_value_for_missing |
|
459 return expand_delimited_tokens(string, expand, delimiters) |
504 return expand_delimited_tokens(string, expand, delimiters) |
460 |
505 |
461 def distinct_array(arr): |
506 def distinct_array(arr): |
462 newarray = [] |
507 newarray = [] |
463 for val in arr: |
508 for val in arr: |
536 if inspect.isclass(modelclass): |
590 if inspect.isclass(modelclass): |
537 if issubclass(modelclass, classinstance): |
591 if issubclass(modelclass, classinstance): |
538 return modelclass |
592 return modelclass |
539 return classinstance |
593 return classinstance |
540 |
594 |
541 class DataMapRef(object): |
595 class OProperty(object): |
542 """ |
596 """Based on the emulation of PyProperty_Type() in Objects/descrobject.c |
543 Utility class for handling map attributes in data section |
597 from http://infinitesque.net/articles/2005/enhancing%20Python%27s%20property.xhtml""" |
544 """ |
598 def __init__(self, fget=None, fset=None, fdel=None, doc=None): |
545 @classmethod |
599 self.fget = fget |
546 def get_feature_ref(cls, map): |
600 self.fset = fset |
547 index = map.find("@") |
601 self.fdel = fdel |
548 if index != -1: |
602 self.__doc__ = doc |
549 parts = map.split("@") |
603 |
550 return parts[0][:-1] |
604 def __get__(self, obj, objtype=None): |
551 else: |
605 if obj is None: |
552 return None |
606 return self |
553 |
607 if self.fget is None: |
554 @classmethod |
608 raise AttributeError, "unreadable attribute" |
555 def get_key_value(cls, map): |
609 if self.fget.__name__ == '<lambda>' or not self.fget.__name__: |
556 index = map.find("@") |
610 return self.fget(obj) |
557 if index != -1: |
611 else: |
558 parts = map.split("@") |
612 return getattr(obj, self.fget.__name__)() |
559 key = parts[1][:-1] |
613 |
560 keys = key.split("=") |
614 def __set__(self, obj, value): |
561 value = keys[1].strip() |
615 if self.fset is None: |
562 return value[1:-1] |
616 raise AttributeError, "can't set attribute" |
563 else: |
617 if self.fset.__name__ == '<lambda>' or not self.fset.__name__: |
564 return None |
618 self.fset(obj, value) |
565 |
619 else: |
|
620 getattr(obj, self.fset.__name__)(value) |
|
621 |
|
622 def __delete__(self, obj): |
|
623 if self.fdel is None: |
|
624 raise AttributeError, "can't delete attribute" |
|
625 if self.fdel.__name__ == '<lambda>' or not self.fdel.__name__: |
|
626 self.fdel(obj) |
|
627 else: |
|
628 getattr(obj, self.fdel.__name__)() |
566 |
629 |
567 class xml(object): |
630 class xml(object): |
568 """ |
631 """ |
569 Class container for set of XML-related helper functions. |
632 Class container for set of XML-related helper functions. |
570 """ |
633 """ |
583 parts = xml_tag[1:].split('}') |
646 parts = xml_tag[1:].split('}') |
584 return (parts[0], parts[1]) |
647 return (parts[0], parts[1]) |
585 else: |
648 else: |
586 return (None, xml_tag) |
649 return (None, xml_tag) |
587 |
650 |
|
651 @classmethod |
|
652 def get_xml_root(cls, resource): |
|
653 """ |
|
654 Get a (namespace, tag) tuple of the root element in the XML data |
|
655 read from the given resource. |
|
656 |
|
657 @param resource: The resource from which to read data. Should be a |
|
658 file-like object (i.e. should have a read() method). |
|
659 @return: A (namespace, tag) tuple. Note that the namespace may |
|
660 be None. |
|
661 |
|
662 @raise exceptions.XmlParseError: The resource contains invalid XML data. |
|
663 """ |
|
664 class RootElementFound(RuntimeError): |
|
665 def __init__(self, root_name): |
|
666 self.root_name = root_name |
|
667 |
|
668 def handle_start(name, attrs): |
|
669 raise RootElementFound(name) |
|
670 |
|
671 p = expat.ParserCreate(namespace_separator=':') |
|
672 p.StartElementHandler = handle_start |
|
673 |
|
674 BUFSIZE = 128 |
|
675 while True: |
|
676 data = resource.read(BUFSIZE) |
|
677 try: |
|
678 p.Parse(data, len(data) < BUFSIZE) |
|
679 except RootElementFound, e: |
|
680 parts = e.root_name.rsplit(':', 1) |
|
681 if len(parts) > 1: |
|
682 return parts[0], parts[1] |
|
683 else: |
|
684 return None, parts[0] |
|
685 except expat.ExpatError, e: |
|
686 raise exceptions.XmlParseError( |
|
687 "XML parse error on line %d: %s" % (e.lineno, e), |
|
688 e.lineno, str(e)) |
|
689 |
|
690 def update_dict(todict, fromdict): |
|
691 """ |
|
692 Merges the elements of two dictionaries together. |
|
693 @param todict: the target dictionary where data is merged. |
|
694 @param fromdict: the source dict where data is read |
|
695 @return: the modified todict. |
|
696 """ |
|
697 for key in fromdict: |
|
698 todict.setdefault(key, []).extend(fromdict[key]) |
|
699 return todict |
|
700 |
588 def log_exception(logger, msg, msg_level=logging.ERROR, traceback_level=logging.DEBUG): |
701 def log_exception(logger, msg, msg_level=logging.ERROR, traceback_level=logging.DEBUG): |
589 """ |
702 """ |
590 Log an exception so that the given message and the exception's |
703 Log an exception so that the given message and the exception's |
591 traceback are logged separately with the given log levels. |
704 traceback are logged separately with the given log levels. |
592 |
705 |
598 Note that this function should be only used in an exception handler. |
711 Note that this function should be only used in an exception handler. |
599 """ |
712 """ |
600 logger.log(msg_level, msg) |
713 logger.log(msg_level, msg) |
601 logger.log(traceback_level, traceback.format_exc()) |
714 logger.log(traceback_level, traceback.format_exc()) |
602 |
715 |
603 def make_content_info(resource, data): |
716 |
604 """ |
717 def grep(string,list): |
605 Factory for ContentInfo |
718 """ |
606 """ |
719 Grep throught the items in the given list to find matching entries. |
607 cnt_inf = None |
720 """ |
608 |
721 expr = re.compile(string) |
609 if resource != None: |
722 return filter(expr.search,list) |
610 guessed_type = mimetypes.guess_type(resource.get_path()) |
723 |
611 mimetype = None |
724 def grep_tuple(string,list): |
612 mimesubtype = None |
725 """ |
613 |
726 Grep throught the items in the given list to find matching entries. |
614 if guessed_type != None: |
727 @return: a list of tuples (index,text) |
615 mimetype, mimesubtype = guessed_type[0].split('/') |
728 """ |
616 |
729 results = [] |
617 if mimetype == 'image' and mimesubtype == 'x-ms-bmp': |
730 expr = re.compile(string) |
618 cnt_inf = api.BmpImageContentInfo(resource, data) |
731 for (index,text) in enumerate(list): |
619 else: |
732 match = expr.search(text) |
620 cnt_inf = api.ContentInfo(mimetype, mimesubtype) |
733 if match != None: |
621 return cnt_inf |
734 results.append((index,match.string)) |
|
735 return results |
|
736 |
|
737 def grep_dict(string,list): |
|
738 """ |
|
739 Grep throught the items in the given list to find matching entries. |
|
740 @return: a dictionary with list index as key and matching text as value. |
|
741 """ |
|
742 results = {} |
|
743 expr = re.compile(string) |
|
744 for (index,text) in enumerate(list): |
|
745 match = expr.search(text) |
|
746 if match != None: |
|
747 results[index] = match.string |
|
748 return results |
|
749 |
|
750 def cmdsplit(s, comments=False, os_name='nt'): |
|
751 """ |
|
752 Copy of shlex split method to allow parsing of command line parameters in operating system specific mode. |
|
753 |
|
754 """ |
|
755 posix = True |
|
756 lex = shlex.shlex(s, posix=posix) |
|
757 lex.whitespace_split = True |
|
758 if not comments: |
|
759 lex.commenters = '' |
|
760 if os_name == 'nt': |
|
761 lex.escape = '^' |
|
762 return list(lex) |
|
763 |
|
764 |
|
765 import sys |
|
766 sys_version = "%d.%d" % (sys.version_info[0],sys.version_info[1]) |
|
767 if sys_version >= "2.6": |
|
768 def relpath(path, start=os.curdir): |
|
769 return os.path.relpath(path, start) |
|
770 else: |
|
771 def relpath(path, start=os.curdir): |
|
772 """Return a relative version of a path""" |
|
773 |
|
774 if not path: |
|
775 raise ValueError("no path specified") |
|
776 start_list = os.path.abspath(start).split(os.sep) |
|
777 path_list = os.path.abspath(path).split(os.sep) |
|
778 if start_list[0].lower() != path_list[0].lower(): |
|
779 unc_path, rest = os.path.splitunc(path) |
|
780 unc_start, rest = os.path.splitunc(start) |
|
781 if bool(unc_path) ^ bool(unc_start): |
|
782 raise ValueError("Cannot mix UNC and non-UNC paths (%s and %s)" |
|
783 % (path, start)) |
|
784 else: |
|
785 raise ValueError("path is on drive %s, start on drive %s" |
|
786 % (path_list[0], start_list[0])) |
|
787 # Work out how much of the filepath is shared by start and path. |
|
788 for i in range(min(len(start_list), len(path_list))): |
|
789 if start_list[i].lower() != path_list[i].lower(): |
|
790 break |
|
791 else: |
|
792 i += 1 |
|
793 |
|
794 rel_list = [os.pardir] * (len(start_list)-i) + path_list[i:] |
|
795 if not rel_list: |
|
796 return os.curdir |
|
797 return os.path.join(*rel_list) |