|
| 1 | +import os |
| 2 | +import json |
| 3 | +import toml |
| 4 | +from configparser import ConfigParser |
| 5 | + |
| 6 | +def parse_gitmodules(gitmodules_path): |
| 7 | + config = ConfigParser() |
| 8 | + config.read(gitmodules_path) |
| 9 | + modules = [] |
| 10 | + for section in config.sections(): |
| 11 | + path = config.get(section, 'path', fallback=None) |
| 12 | + url = config.get(section, 'url', fallback=None) |
| 13 | + # Extract the library ID from the path |
| 14 | + library_id = path.replace('xai_components/xai_', '').upper() |
| 15 | + if path and url: |
| 16 | + modules.append({ |
| 17 | + 'path': os.path.normpath(path), |
| 18 | + 'url': url, |
| 19 | + 'library_id': library_id |
| 20 | + }) |
| 21 | + return modules |
| 22 | + |
| 23 | +def parse_toml_file(toml_path): |
| 24 | + try: |
| 25 | + with open(toml_path, 'r') as toml_file: |
| 26 | + data = toml.load(toml_file) |
| 27 | + return data |
| 28 | + except Exception as e: |
| 29 | + print(f"Error parsing TOML file at {toml_path}: {e}") |
| 30 | + return None |
| 31 | + |
| 32 | +def read_file_lines_to_list(file_path): |
| 33 | + if not os.path.exists(file_path): |
| 34 | + return [] |
| 35 | + with open(file_path, 'r') as file: |
| 36 | + return [line.strip() for line in file.readlines()] |
| 37 | + |
| 38 | +def extract_library_info(lib_path, base_path, status="installed"): |
| 39 | + relative_lib_path = os.path.join(base_path, os.path.relpath(lib_path, start=base_path)) |
| 40 | + toml_path = os.path.join(lib_path, 'pyproject.toml') |
| 41 | + |
| 42 | + if not os.path.exists(toml_path): |
| 43 | + return None |
| 44 | + |
| 45 | + toml_data = parse_toml_file(toml_path) |
| 46 | + |
| 47 | + # Check if TOML data was successfully parsed |
| 48 | + if toml_data is None: |
| 49 | + return None |
| 50 | + |
| 51 | + # Remove 'xai_' or 'xai-' prefix and convert to uppercase |
| 52 | + library_id = toml_data["project"]["name"].replace("xai_", "").replace("xai-", "").upper() |
| 53 | + |
| 54 | + requirements_rel_path = toml_data["tool"]["xircuits"].get("requirements_path", None) |
| 55 | + requirements_path = None |
| 56 | + requirements = [] |
| 57 | + |
| 58 | + if requirements_rel_path is not None: |
| 59 | + requirements_path = os.path.join(lib_path, requirements_rel_path) |
| 60 | + if os.path.isfile(requirements_path): |
| 61 | + requirements = read_file_lines_to_list(requirements_path) |
| 62 | + else: |
| 63 | + requirements_path = None # Reset to None if the file does not exist |
| 64 | + |
| 65 | + lib_info = { |
| 66 | + "name": toml_data["project"]["name"], |
| 67 | + "library_id": library_id, |
| 68 | + "version": toml_data["project"].get("version", "N/A"), |
| 69 | + "description": toml_data["project"].get("description", "No description available."), |
| 70 | + "authors": toml_data["project"].get("authors", []), |
| 71 | + "license": toml_data["project"].get("license", "N/A"), |
| 72 | + "readme": toml_data["project"].get("readme", None), |
| 73 | + "repository": toml_data["project"].get("repository", None), |
| 74 | + "keywords": toml_data["project"].get("keywords", []), |
| 75 | + "local_path": relative_lib_path, |
| 76 | + "status": status, |
| 77 | + "requirements_path": requirements_path, |
| 78 | + "requirements": requirements, |
| 79 | + "default_example_path": toml_data["tool"]["xircuits"].get("default_example_path", None), |
| 80 | + } |
| 81 | + |
| 82 | + return lib_info |
| 83 | + |
| 84 | +def generate_component_library_config(base_path="xai_components", gitmodules_path=".gitmodules"): |
| 85 | + |
| 86 | + if not os.path.exists(gitmodules_path): |
| 87 | + # Construct the .xircuits/.gitmodules path |
| 88 | + gitmodules_path = os.path.join('.xircuits', '.gitmodules') |
| 89 | + |
| 90 | + libraries = {} |
| 91 | + library_id_map = {} # Map library IDs to library info |
| 92 | + |
| 93 | + # Parse submodules first and set them as "remote" |
| 94 | + if os.path.exists(gitmodules_path): |
| 95 | + submodules = parse_gitmodules(gitmodules_path) |
| 96 | + for submodule in submodules: |
| 97 | + submodule_path = os.path.normpath(submodule['path']) |
| 98 | + library_info = { |
| 99 | + "name": os.path.basename(submodule_path), |
| 100 | + "library_id": submodule['library_id'], # Use the library ID from the submodule info |
| 101 | + "repository": submodule['url'], |
| 102 | + "local_path": submodule_path, |
| 103 | + "status": "remote" |
| 104 | + } |
| 105 | + libraries[submodule_path] = library_info |
| 106 | + library_id_map[submodule['library_id']] = library_info |
| 107 | + |
| 108 | + def explore_directory(directory, base_path): |
| 109 | + for item in os.listdir(directory): |
| 110 | + full_path = os.path.normpath(os.path.join(directory, item)) |
| 111 | + if os.path.isdir(full_path) and item.startswith("xai_"): |
| 112 | + lib_info = extract_library_info(full_path, base_path) |
| 113 | + if lib_info: # If a valid pyproject.toml is found |
| 114 | + if lib_info['library_id'] in library_id_map: # Match by library ID |
| 115 | + # Update the existing entry with the new info |
| 116 | + library_id_map[lib_info['library_id']].update(lib_info) |
| 117 | + else: |
| 118 | + libraries[full_path] = lib_info # Add new library info |
| 119 | + # Recursively explore subdirectories |
| 120 | + explore_directory(full_path, base_path) |
| 121 | + |
| 122 | + explore_directory(base_path, base_path) |
| 123 | + |
| 124 | + return {"libraries": list(libraries.values())} |
| 125 | + |
| 126 | +def save_component_library_config(filename=".xircuits/component_library_config.json"): |
| 127 | + libraries_data = generate_component_library_config() |
| 128 | + os.makedirs(os.path.dirname(filename), exist_ok=True) |
| 129 | + with open(filename, 'w') as json_file: |
| 130 | + json.dump(libraries_data, json_file, indent=4) |
| 131 | + |
| 132 | +if __name__ == "__main__": |
| 133 | + save_component_library_config() |
0 commit comments