nodes: 0, module; 1, function_definition; 2, function_name:ComputeApplicationUniquifier; 3, parameters; 4, block; 5, identifier:hash_obj; 6, expression_statement; 7, function_definition; 8, function_definition; 9, function_definition; 10, expression_statement; 11, comment:"""Computes hash of application files.
  
    Application files can be anywhere on the disk. The application is free to
    import a Python module from an arbitrary path ok the disk. It is also
    impossible to distinguish application files from third party libraries.
    Third party libraries are typically installed with "pip" and there is not a
    good way to guarantee that all instances of the application are going to have
    exactly the same version of each package. There is also a huge amount of files
    in all sys.path directories and it will take too much time to traverse them
    all. We therefore make an assumption that application files are only located
    in sys.path[0].
  
    When traversing files in sys.path, we can expect both .py and .pyc files. For
    source deployment, we will find both .py and .pyc files. In this case we will
    only index .py files and ignored .pyc file. In case of binary deployment, only
    .pyc file will be there.
  
    The naive way to hash files would be to read the file content and compute some
    sort of a hash (e.g. SHA1). This can be expensive as well, so instead we just
    hash file name and file size. It is a good enough heuristics to identify
    modified files across different deployments.
  
    Args:
      hash_obj: hash aggregator to update with application uniquifier.
    """; 12, function_name:ProcessDirectory; 13, parameters; 14, block; 15, function_name:IsPackage; 16, parameters; 17, block; 18, function_name:ProcessApplicationFile; 19, parameters; 20, block; 21, call; 22, identifier:path; 23, identifier:relative_path; 24, default_parameter; 25, expression_statement; 26, if_statement; 27, try_statement; 28, comment:# Sort file names to ensure consistent hash regardless of order returned; 29, comment:# by os.listdir. This will also put .py files before .pyc and .pyo files.; 30, expression_statement; 31, for_statement; 32, identifier:path; 33, expression_statement; 34, expression_statement; 35, return_statement; 36, identifier:path; 37, identifier:relative_path; 38, expression_statement; 39, expression_statement; 40, expression_statement; 41, try_statement; 42, expression_statement; 43, identifier:ProcessDirectory; 44, argument_list; 45, identifier:depth; 46, integer:1; 47, comment:"""Recursively computes application uniquifier for a particular directory.
  
      Args:
        path: absolute path of the directory to start.
        relative_path: path relative to sys.path[0]
        depth: current recursion depth.
      """; 48, comparison_operator:depth > _MAX_DEPTH; 49, block; 50, block; 51, except_clause; 52, assignment; 53, identifier:name; 54, call; 55, block; 56, comment:"""Checks if the specified directory is a valid Python package."""; 57, assignment; 58, parenthesized_expression; 59, comment:"""Updates the hash with the specified application file."""; 60, call; 61, call; 62, block; 63, except_clause; 64, call; 65, subscript; 66, string; 67, identifier:depth; 68, identifier:_MAX_DEPTH; 69, return_statement; 70, expression_statement; 71, identifier:BaseException; 72, block; 73, identifier:modules; 74, call; 75, identifier:sorted; 76, argument_list; 77, expression_statement; 78, if_statement; 79, identifier:init_base_path; 80, call; 81, boolean_operator; 82, attribute; 83, argument_list; 84, attribute; 85, argument_list; 86, expression_statement; 87, identifier:BaseException; 88, block; 89, attribute; 90, argument_list; 91, attribute; 92, integer:0; 93, assignment; 94, return_statement; 95, identifier:set; 96, argument_list; 97, identifier:names; 98, assignment; 99, not_operator; 100, block; 101, elif_clause; 102, attribute; 103, argument_list; 104, boolean_operator; 105, call; 106, identifier:hash_obj; 107, identifier:update; 108, call; 109, identifier:hash_obj; 110, identifier:update; 111, call; 112, call; 113, pass_statement; 114, identifier:hash_obj; 115, identifier:update; 116, call; 117, identifier:sys; 118, identifier:path; 119, identifier:names; 120, call; 121, identifier:current_path; 122, call; 123, call; 124, expression_statement; 125, if_statement; 126, if_statement; 127, expression_statement; 128, expression_statement; 129, call; 130, block; 131, attribute; 132, identifier:join; 133, identifier:path; 134, string; 135, call; 136, call; 137, attribute; 138, argument_list; 139, attribute; 140, argument_list; 141, attribute; 142, argument_list; 143, attribute; 144, argument_list; 145, attribute; 146, argument_list; 147, attribute; 148, argument_list; 149, attribute; 150, argument_list; 151, attribute; 152, argument_list; 153, assignment; 154, comparison_operator:ext not in ('.py', '.pyc', '.pyo'); 155, block; 156, comparison_operator:file_name in modules; 157, block; 158, call; 159, call; 160, identifier:IsPackage; 161, argument_list; 162, expression_statement; 163, identifier:os; 164, identifier:path; 165, string_content:__init__.py; 166, attribute; 167, argument_list; 168, attribute; 169, argument_list; 170, attribute; 171, identifier:isfile; 172, binary_operator:init_base_path + 'o'; 173, identifier:relative_path; 174, identifier:encode; 175, string; 176, identifier:encode; 177, identifier:hash_obj; 178, identifier:update; 179, call; 180, string; 181, identifier:encode; 182, identifier:os; 183, identifier:listdir; 184, identifier:path; 185, attribute; 186, identifier:join; 187, identifier:path; 188, identifier:name; 189, attribute; 190, identifier:isdir; 191, identifier:current_path; 192, pattern_list; 193, call; 194, identifier:ext; 195, tuple; 196, continue_statement; 197, comment:# This is not an application file.; 198, identifier:file_name; 199, identifier:modules; 200, continue_statement; 201, comment:# This is a .pyc file and we already indexed .py file.; 202, attribute; 203, argument_list; 204, identifier:ProcessApplicationFile; 205, argument_list; 206, identifier:current_path; 207, call; 208, attribute; 209, identifier:isfile; 210, identifier:init_base_path; 211, attribute; 212, identifier:isfile; 213, binary_operator:init_base_path + 'c'; 214, identifier:os; 215, identifier:path; 216, identifier:init_base_path; 217, string; 218, string_content::; 219, attribute; 220, argument_list; 221, string_content; 222, identifier:os; 223, identifier:path; 224, identifier:os; 225, identifier:path; 226, identifier:file_name; 227, identifier:ext; 228, attribute; 229, argument_list; 230, string; 231, string; 232, string; 233, identifier:modules; 234, identifier:add; 235, identifier:file_name; 236, identifier:current_path; 237, call; 238, identifier:ProcessDirectory; 239, argument_list; 240, identifier:os; 241, identifier:path; 242, identifier:os; 243, identifier:path; 244, identifier:init_base_path; 245, string; 246, string_content:o; 247, call; 248, identifier:encode; 249, escape_sequence:\n; 250, attribute; 251, identifier:splitext; 252, identifier:name; 253, string_content:.py; 254, string_content:.pyc; 255, string_content:.pyo; 256, attribute; 257, argument_list; 258, identifier:current_path; 259, call; 260, binary_operator:depth + 1; 261, string_content:c; 262, identifier:str; 263, argument_list; 264, identifier:os; 265, identifier:path; 266, attribute; 267, identifier:join; 268, identifier:relative_path; 269, identifier:name; 270, attribute; 271, argument_list; 272, identifier:depth; 273, integer:1; 274, attribute; 275, identifier:os; 276, identifier:path; 277, attribute; 278, identifier:join; 279, identifier:relative_path; 280, identifier:name; 281, call; 282, identifier:st_size; 283, identifier:os; 284, identifier:path; 285, attribute; 286, argument_list; 287, identifier:os; 288, identifier:stat; 289, identifier:path
code: def ComputeApplicationUniquifier(hash_obj):
    """Computes hash of application files.
  
    Application files can be anywhere on the disk. The application is free to
    import a Python module from an arbitrary path ok the disk. It is also
    impossible to distinguish application files from third party libraries.
    Third party libraries are typically installed with "pip" and there is not a
    good way to guarantee that all instances of the application are going to have
    exactly the same version of each package. There is also a huge amount of files
    in all sys.path directories and it will take too much time to traverse them
    all. We therefore make an assumption that application files are only located
    in sys.path[0].
  
    When traversing files in sys.path, we can expect both .py and .pyc files. For
    source deployment, we will find both .py and .pyc files. In this case we will
    only index .py files and ignored .pyc file. In case of binary deployment, only
    .pyc file will be there.
  
    The naive way to hash files would be to read the file content and compute some
    sort of a hash (e.g. SHA1). This can be expensive as well, so instead we just
    hash file name and file size. It is a good enough heuristics to identify
    modified files across different deployments.
  
    Args:
      hash_obj: hash aggregator to update with application uniquifier.
    """
  
    def ProcessDirectory(path, relative_path, depth=1):
      """Recursively computes application uniquifier for a particular directory.
  
      Args:
        path: absolute path of the directory to start.
        relative_path: path relative to sys.path[0]
        depth: current recursion depth.
      """
  
      if depth > _MAX_DEPTH:
        return
  
      try:
        names = os.listdir(path)
      except BaseException:
        return
  
      # Sort file names to ensure consistent hash regardless of order returned
      # by os.listdir. This will also put .py files before .pyc and .pyo files.
      modules = set()
      for name in sorted(names):
        current_path = os.path.join(path, name)
        if not os.path.isdir(current_path):
          file_name, ext = os.path.splitext(name)
          if ext not in ('.py', '.pyc', '.pyo'):
            continue  # This is not an application file.
          if file_name in modules:
            continue  # This is a .pyc file and we already indexed .py file.
  
          modules.add(file_name)
          ProcessApplicationFile(current_path, os.path.join(relative_path, name))
        elif IsPackage(current_path):
          ProcessDirectory(current_path,
                           os.path.join(relative_path, name),
                           depth + 1)
  
    def IsPackage(path):
      """Checks if the specified directory is a valid Python package."""
      init_base_path = os.path.join(path, '__init__.py')
      return (os.path.isfile(init_base_path) or
              os.path.isfile(init_base_path + 'c') or
              os.path.isfile(init_base_path + 'o'))
  
    def ProcessApplicationFile(path, relative_path):
      """Updates the hash with the specified application file."""
      hash_obj.update(relative_path.encode())
      hash_obj.update(':'.encode())
      try:
        hash_obj.update(str(os.stat(path).st_size).encode())
      except BaseException:
        pass
      hash_obj.update('\n'.encode())
  
    ProcessDirectory(sys.path[0], '')