#NEWLINE# @ classmethod #NEWLINE# def create_from_spec( cls , ftype , canonical_at , filespec ) : #NEWLINE# #INDENT# 'Given a filespec, return a PathGlob object.\n\n    :param ftype: The Stat subclass intended to be matched by this PathGlobs. TODO: this\n      value is only used by the Scheduler currently, which is weird. Move to a wrapper?\n    :param canonical_at: A path relative to the ProjectTree that is known to be\n      canonical. This requirement exists in order to avoid "accidentally"\n      traversing symlinks during expansion of a PathGlobs, which would break filesystem\n      invalidation. TODO: Consider asserting that this is a `realpath`.\n    :param filespec: A filespec, relative to canonical_at.\n\n    TODO: Because `create_from_spec` is called recursively, this method should work harder to\n    avoid splitting/joining. Optimization needed.\n    ' #NEWLINE# parts = _norm_with_dir ( filespec ) . split ( os_sep ) #NEWLINE# if ( cls . _DOUBLE in parts [ 0 ] ) : #NEWLINE# #INDENT# if ( parts [ 0 ] != cls . _DOUBLE ) : #NEWLINE# #INDENT# raise ValueError ( 'Illegal component "{}" in filespec: {}' . format ( parts [ 0 ] , join ( canonical_at , filespec ) ) ) #NEWLINE# #UNINDENT# remainders = ( join ( * parts [ 1 : ] ) , join ( * parts [ 0 : ] ) ) #NEWLINE# return PathDirWildcard ( ftype , canonical_at , parts [ 0 ] , remainders ) #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# if ( cls . _SINGLE not in parts [ 0 ] ) : #NEWLINE# #INDENT# if ( len ( parts ) == 1 ) : #NEWLINE# #INDENT# return Path ( join ( canonical_at , parts [ 0 ] ) ) #NEWLINE# #UNINDENT# return PathLiteral ( ftype , join ( canonical_at , parts [ 0 ] ) , join ( * parts [ 1 : ] ) ) #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# if ( len ( parts ) == 1 ) : #NEWLINE# #INDENT# return PathWildcard ( canonical_at , parts [ 0 ] ) #NEWLINE# #UNINDENT# else : #NEWLINE# #INDENT# remainders = ( join ( * parts [ 1 : ] ) , ) #NEWLINE# return PathDirWildcard ( ftype , canonical_at , parts [ 0 ] , remainders )