|
3 | 3 |
|
4 | 4 | import tomlkit
|
5 | 5 |
|
6 |
| -from finecode_extension_api.interfaces import ifilemanager, ipypackagelayoutinfoprovider |
| 6 | +from finecode_extension_api.interfaces import ifilemanager, ipypackagelayoutinfoprovider, icache |
7 | 7 | from finecode_extension_api import service
|
8 | 8 |
|
9 | 9 |
|
| 10 | +class ConfigParseError(Exception): |
| 11 | + def __init__(self, message: str) -> None: |
| 12 | + self.message = message |
| 13 | + |
| 14 | + |
10 | 15 | class PyPackageLayoutInfoProvider(ipypackagelayoutinfoprovider.IPyPackageLayoutInfoProvider, service.Service):
|
11 |
| - def __init__(self, file_manager: ifilemanager.IFileManager) -> None: |
| 16 | + PACKAGE_NAME_CACHE_KEY = 'PyPackageLayoutInfoProviderPackageName' |
| 17 | + |
| 18 | + def __init__(self, file_manager: ifilemanager.IFileManager, cache: icache.ICache) -> None: |
12 | 19 | self.file_manager = file_manager
|
13 |
| - # TODO: cache package name by file version? |
| 20 | + self.cache = cache |
14 | 21 |
|
15 | 22 | async def _get_package_name(self, package_dir_path: pathlib.Path) -> str:
|
| 23 | + # raises ConfigParseError |
16 | 24 | package_def_file = package_dir_path / 'pyproject.toml'
|
17 | 25 | if not package_def_file.exists():
|
18 | 26 | raise NotImplementedError("Only python packages with pyproject.toml config file are supported")
|
19 | 27 |
|
| 28 | + try: |
| 29 | + cached_package_name = await self.cache.get_file_cache(file_path=package_def_file, key=self.PACKAGE_NAME_CACHE_KEY) |
| 30 | + return cached_package_name |
| 31 | + except icache.CacheMissException: |
| 32 | + ... |
| 33 | + |
20 | 34 | package_def_file_content = await self.file_manager.get_content(file_path=package_def_file)
|
21 |
| - # TODO: handle errors |
22 |
| - package_def_dict = tomlkit.loads(package_def_file_content) |
| 35 | + package_def_file_version = await self.file_manager.get_file_version(file_path=package_def_file) |
| 36 | + try: |
| 37 | + package_def_dict = tomlkit.loads(package_def_file_content) |
| 38 | + except tomlkit.exceptions.ParseError as exception: |
| 39 | + raise ConfigParseError(f"Failed to parse package config {package_def_file}: {exception.message} at {exception.line}:{exception.col}") |
| 40 | + |
23 | 41 | package_raw_name = package_def_dict.get('project', {}).get('name', None)
|
24 | 42 | if package_raw_name is None:
|
25 | 43 | raise ValueError(f"package.name not found in {package_def_file}")
|
26 | 44 |
|
27 |
| - return package_raw_name.replace('-', '_') |
| 45 | + package_name = package_raw_name.replace('-', '_') |
| 46 | + await self.cache.save_file_cache(file_path=package_def_file, file_version=package_def_file_version, key=self.PACKAGE_NAME_CACHE_KEY, value=package_name) |
| 47 | + return package_name |
28 | 48 |
|
29 | 49 | async def get_package_layout(self, package_dir_path: pathlib.Path) -> ipypackagelayoutinfoprovider.PyPackageLayout:
|
30 | 50 | if (package_dir_path / 'src').exists():
|
31 | 51 | return ipypackagelayoutinfoprovider.PyPackageLayout.SRC
|
32 | 52 | else:
|
33 |
| - package_name = await self._get_package_name(package_dir_path=package_dir_path) |
| 53 | + try: |
| 54 | + package_name = await self._get_package_name(package_dir_path=package_dir_path) |
| 55 | + except ConfigParseError as exception: |
| 56 | + raise ipypackagelayoutinfoprovider.FailedToGetPackageLayout(exception.message) |
| 57 | + |
34 | 58 | if (package_dir_path / package_name).exists():
|
35 | 59 | return ipypackagelayoutinfoprovider.PyPackageLayout.FLAT
|
36 | 60 | else:
|
37 | 61 | return ipypackagelayoutinfoprovider.PyPackageLayout.CUSTOM
|
38 | 62 |
|
39 | 63 | async def get_package_src_root_dir_path(self, package_dir_path: str) -> pathlib.Path:
|
40 |
| - package_layout = await self.get_package_layout(package_dir_path=package_dir_path) |
41 |
| - package_name = await self._get_package_name(package_dir_path=package_dir_path) |
| 64 | + try: |
| 65 | + package_layout = await self.get_package_layout(package_dir_path=package_dir_path) |
| 66 | + except ipypackagelayoutinfoprovider.FailedToGetPackageLayout as exception: |
| 67 | + raise ipypackagelayoutinfoprovider.FailedToGetPackageSrcRootDirPath(exception.message) |
| 68 | + |
| 69 | + try: |
| 70 | + package_name = await self._get_package_name(package_dir_path=package_dir_path) |
| 71 | + except ConfigParseError as exception: |
| 72 | + raise ipypackagelayoutinfoprovider.FailedToGetPackageSrcRootDirPath(exception.message) |
| 73 | + |
42 | 74 | if package_layout == ipypackagelayoutinfoprovider.PyPackageLayout.SRC:
|
43 | 75 | return package_dir_path / 'src' / package_name
|
44 | 76 | elif package_layout == ipypackagelayoutinfoprovider.PyPackageLayout.FLAT:
|
|
0 commit comments