coiled 1.122.0__tar.gz → 1.124.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of coiled might be problematic. Click here for more details.
- {coiled-1.122.0 → coiled-1.124.0}/PKG-INFO +1 -1
- {coiled-1.122.0 → coiled-1.124.0}/coiled/filestore.py +16 -7
- {coiled-1.122.0 → coiled-1.124.0}/coiled/software_utils.py +134 -5
- {coiled-1.122.0 → coiled-1.124.0}/.gitignore +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/LICENSE +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/README.md +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/__main__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/analytics.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/auth.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/batch.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/capture_environment.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/batch/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/batch/list.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/batch/logs.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/batch/run.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/batch/status.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/batch/wait.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/cluster/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/cluster/azure_logs.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/cluster/better_logs.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/cluster/crud.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/cluster/get_address.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/cluster/list.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/cluster/logs.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/cluster/metrics.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/cluster/ssh.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/cluster/utils.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/config.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/core.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/curl.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/diagnostics.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/env.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/file.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/examples/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/examples/exit.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/examples/hello_world.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/examples/nyc_parquet.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/examples/pytorch.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/examples/xarray_nwm.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/hello.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/scripts/fill_ipython.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/scripts/nyc_parquet.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/scripts/pytorch.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/scripts/xarray_nwm.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/hello/utils.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/login.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/notebook/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/notebook/notebook.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/package_sync.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/prefect.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/prefect_serve.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/run.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/setup/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/setup/amp.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/setup/aws.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/setup/azure.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/setup/entry.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/setup/gcp.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/setup/prometheus.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/setup/util.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/sync.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cli/utils.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/cluster.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/coiled.yaml +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/compatibility.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/config.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/context.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/core.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/credentials/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/credentials/aws.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/credentials/google.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/errors.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/exceptions.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/extensions/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/extensions/prefect/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/extensions/prefect/runners.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/extensions/prefect/workers.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/function.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/plugins.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/prefect.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/pypi_conda_map.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/scan.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/software.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/spans.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/spark.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/types.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/utils.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/v2/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/v2/cluster.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/v2/core.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/v2/cwi_log_link.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/v2/states.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/v2/widgets/__init__.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/v2/widgets/interface.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/v2/widgets/rich.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/v2/widgets/util.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/coiled/websockets.py +0 -0
- {coiled-1.122.0 → coiled-1.124.0}/pyproject.toml +0 -0
|
@@ -135,13 +135,24 @@ def upload_to_filestore_with_ui(fs, local_dir):
|
|
|
135
135
|
},
|
|
136
136
|
])
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
upload_info = FilestoreManager.get_signed_upload_urls(fs["id"], files_for_upload=files)
|
|
139
|
+
|
|
140
|
+
upload_urls = upload_info.get("urls")
|
|
141
|
+
existing_blobs = upload_info.get("existing")
|
|
139
142
|
|
|
140
143
|
for local_path, relative_path, size in files:
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
skip_upload = False
|
|
145
|
+
existing_blob_info = existing_blobs.get(relative_path)
|
|
146
|
+
if existing_blob_info:
|
|
147
|
+
modified = os.path.getmtime(local_path)
|
|
148
|
+
if size == existing_blob_info["size"] and modified < existing_blob_info["modified"]:
|
|
149
|
+
skip_upload = True
|
|
150
|
+
|
|
151
|
+
if not skip_upload:
|
|
152
|
+
progress.batch_title = progress_title(local_path)
|
|
153
|
+
progress.refresh()
|
|
143
154
|
|
|
144
|
-
|
|
155
|
+
FilestoreManager.upload_to_signed_url(local_path, upload_urls[relative_path])
|
|
145
156
|
|
|
146
157
|
done_files += 1
|
|
147
158
|
done_bytes += size
|
|
@@ -201,9 +212,7 @@ class FilestoreManagerWithoutHttp:
|
|
|
201
212
|
@classmethod
|
|
202
213
|
def get_signed_upload_urls(cls, fs_id, files_for_upload):
|
|
203
214
|
paths = [p for _, p, _ in files_for_upload] # relative paths
|
|
204
|
-
return cls.make_req(f"/api/v2/filestore/fs/{fs_id}/signed-urls/upload", post=True, data={"paths": paths})
|
|
205
|
-
"urls"
|
|
206
|
-
)
|
|
215
|
+
return cls.make_req(f"/api/v2/filestore/fs/{fs_id}/signed-urls/upload", post=True, data={"paths": paths})
|
|
207
216
|
|
|
208
217
|
@classmethod
|
|
209
218
|
def get_download_list_with_urls(cls, fs_id):
|
|
@@ -55,6 +55,7 @@ subdir_datas = {}
|
|
|
55
55
|
|
|
56
56
|
ANY_AVAILABLE = "ANY-AVAILABLE"
|
|
57
57
|
AUTH_BEARER_USERNAME = "AUTH_BEARER_TOKEN"
|
|
58
|
+
CONDA_TOKEN_USERNAME = "CONDA_TOKEN"
|
|
58
59
|
COILED_LOCAL_PACKAGE_PREFIX = "coiled_local_"
|
|
59
60
|
DEFAULT_JSON_PYPI_URL = "https://pypi.org/pypi"
|
|
60
61
|
DEFAULT_PYPI_URL = "https://pypi.org/simple"
|
|
@@ -573,7 +574,9 @@ def get_mamba_auth_dict(home_dir: Path | None = None) -> dict[str, tuple[str, st
|
|
|
573
574
|
auth_data = json.load(f)
|
|
574
575
|
for domain, auth in auth_data.items():
|
|
575
576
|
auth_type = auth.get("type")
|
|
576
|
-
if auth_type == "CondaToken"
|
|
577
|
+
if auth_type == "CondaToken":
|
|
578
|
+
domain_auth[domain] = (CONDA_TOKEN_USERNAME, auth["token"])
|
|
579
|
+
elif auth_type == "BearerToken":
|
|
577
580
|
domain_auth[domain] = (AUTH_BEARER_USERNAME, auth["token"])
|
|
578
581
|
elif auth_type == "BasicHTTPAuthentication":
|
|
579
582
|
domain_auth[domain] = (
|
|
@@ -592,6 +595,126 @@ def get_mamba_auth(netloc: str) -> tuple[str, str] | None:
|
|
|
592
595
|
return get_mamba_auth_dict().get(netloc, None)
|
|
593
596
|
|
|
594
597
|
|
|
598
|
+
def _parse_rattler_auth_data(auth_data: dict) -> tuple[str, str] | None:
|
|
599
|
+
"""Parse rattler authentication data into username/password tuple.
|
|
600
|
+
|
|
601
|
+
Handles rattler Authentication variants:
|
|
602
|
+
- {"BearerToken": "token"}
|
|
603
|
+
- {"CondaToken": "token"}
|
|
604
|
+
- {"BasicHTTP": {"username": "user", "password": "pass"}}
|
|
605
|
+
|
|
606
|
+
Returns:
|
|
607
|
+
Tuple of (username, password) or None if unknown auth type
|
|
608
|
+
"""
|
|
609
|
+
if "BearerToken" in auth_data:
|
|
610
|
+
return (AUTH_BEARER_USERNAME, auth_data["BearerToken"])
|
|
611
|
+
elif "CondaToken" in auth_data:
|
|
612
|
+
return (CONDA_TOKEN_USERNAME, auth_data["CondaToken"])
|
|
613
|
+
elif "BasicHTTP" in auth_data:
|
|
614
|
+
basic_auth = auth_data["BasicHTTP"]
|
|
615
|
+
return (
|
|
616
|
+
basic_auth.get("username", ""),
|
|
617
|
+
basic_auth.get("password", ""),
|
|
618
|
+
)
|
|
619
|
+
else:
|
|
620
|
+
return None
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
@functools.lru_cache
|
|
624
|
+
def get_rattler_auth_dict(home_dir: Path | None = None) -> dict[str, tuple[str, str]]:
|
|
625
|
+
# RATTLER_AUTH_FILE is an env var that can override the default location
|
|
626
|
+
env_auth_file = os.environ.get("RATTLER_AUTH_FILE")
|
|
627
|
+
if env_auth_file:
|
|
628
|
+
auth_file = Path(env_auth_file)
|
|
629
|
+
else:
|
|
630
|
+
if home_dir is None:
|
|
631
|
+
home_dir = Path.home()
|
|
632
|
+
auth_file = home_dir / ".rattler" / "credentials.json"
|
|
633
|
+
domain_auth = {}
|
|
634
|
+
if auth_file.exists():
|
|
635
|
+
with auth_file.open("r") as f:
|
|
636
|
+
auth_data = json.load(f)
|
|
637
|
+
for domain, auth in auth_data.items():
|
|
638
|
+
parsed_auth = _parse_rattler_auth_data(auth)
|
|
639
|
+
if parsed_auth:
|
|
640
|
+
domain_auth[domain] = parsed_auth
|
|
641
|
+
else:
|
|
642
|
+
logger.debug(f"Encountered unknown rattler auth type {list(auth.keys())} for domain {domain}")
|
|
643
|
+
return domain_auth
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def get_rattler_keyring_auth(netloc: str) -> tuple[str, str] | None:
|
|
647
|
+
"""Returns the Requests tuple auth for a given domain from rattler keyring storage."""
|
|
648
|
+
if not HAVE_KEYRING:
|
|
649
|
+
logger.debug("keyring not available, skipping rattler keyring auth")
|
|
650
|
+
return None
|
|
651
|
+
|
|
652
|
+
def try_keyring_auth(host: str) -> tuple[str, str] | None:
|
|
653
|
+
"""Try to get auth from keyring for a specific host using rattler's storage format."""
|
|
654
|
+
try:
|
|
655
|
+
# Use the existing get_keyring_auth function with "rattler" as the URL
|
|
656
|
+
# and the host as the username to get rattler-stored credentials
|
|
657
|
+
auth_parts = get_keyring_auth("rattler", host)
|
|
658
|
+
if auth_parts:
|
|
659
|
+
username, password = auth_parts
|
|
660
|
+
if password:
|
|
661
|
+
try:
|
|
662
|
+
auth_data = json.loads(password)
|
|
663
|
+
parsed_auth = _parse_rattler_auth_data(auth_data)
|
|
664
|
+
if parsed_auth:
|
|
665
|
+
return parsed_auth
|
|
666
|
+
except json.JSONDecodeError:
|
|
667
|
+
# If it's not JSON, treat it as a simple username/password
|
|
668
|
+
return (username or host, password)
|
|
669
|
+
|
|
670
|
+
except Exception as e:
|
|
671
|
+
logger.debug(f"Error getting rattler keyring auth for {host}: {e}")
|
|
672
|
+
return None
|
|
673
|
+
|
|
674
|
+
return None
|
|
675
|
+
|
|
676
|
+
# Try exact match first
|
|
677
|
+
auth_parts = try_keyring_auth(netloc)
|
|
678
|
+
if auth_parts:
|
|
679
|
+
logger.debug(f"Found rattler keyring auth for {netloc}")
|
|
680
|
+
return auth_parts
|
|
681
|
+
|
|
682
|
+
# Try parent domain matches if exact match failed
|
|
683
|
+
# If looking for foo.example.com, try example.com (but not com)
|
|
684
|
+
parts = netloc.split(".")
|
|
685
|
+
for i in range(1, len(parts) - 1): # Stop before single TLD
|
|
686
|
+
parent_domain = ".".join(parts[i:])
|
|
687
|
+
|
|
688
|
+
auth_parts = try_keyring_auth(parent_domain)
|
|
689
|
+
if auth_parts:
|
|
690
|
+
logger.debug(f"Found rattler keyring auth for {parent_domain} (matching {netloc})")
|
|
691
|
+
return auth_parts
|
|
692
|
+
|
|
693
|
+
logger.debug(f"No rattler keyring auth found for {netloc}")
|
|
694
|
+
return None
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
def get_rattler_auth(netloc: str) -> tuple[str, str] | None:
|
|
698
|
+
"""Returns the Requests tuple auth for a given domain from rattler keyring or auth file."""
|
|
699
|
+
# Try keyring first (primary storage method for rattler/pixi)
|
|
700
|
+
auth_parts = get_rattler_keyring_auth(netloc)
|
|
701
|
+
if auth_parts:
|
|
702
|
+
return auth_parts
|
|
703
|
+
|
|
704
|
+
# Fall back to file-based storage
|
|
705
|
+
# rattler allows wildcards, so we have to check for exact matches first
|
|
706
|
+
auth_parts = get_rattler_auth_dict().get(netloc, None)
|
|
707
|
+
if auth_parts:
|
|
708
|
+
return auth_parts
|
|
709
|
+
|
|
710
|
+
# then check for wildcard matches in file storage
|
|
711
|
+
for domain, auth in get_rattler_auth_dict().items():
|
|
712
|
+
if domain.startswith("*.") and netloc.endswith(domain[1:]):
|
|
713
|
+
return auth
|
|
714
|
+
|
|
715
|
+
return None
|
|
716
|
+
|
|
717
|
+
|
|
595
718
|
@functools.lru_cache
|
|
596
719
|
def get_conda_config() -> dict:
|
|
597
720
|
"""Returns the current conda config as dictionary"""
|
|
@@ -706,7 +829,7 @@ print(json.dumps(auth_parts))
|
|
|
706
829
|
return None
|
|
707
830
|
|
|
708
831
|
if auth_type == "token":
|
|
709
|
-
username =
|
|
832
|
+
username = CONDA_TOKEN_USERNAME
|
|
710
833
|
|
|
711
834
|
if not username and not password:
|
|
712
835
|
return None
|
|
@@ -743,6 +866,7 @@ def set_auth_for_url(url: Url | str) -> str:
|
|
|
743
866
|
use_keyring = dask.config.get("coiled.package_sync.conda.cred_sources.keyring", True)
|
|
744
867
|
use_conda_auth = dask.config.get("coiled.package_sync.conda.cred_sources.conda", True)
|
|
745
868
|
use_mamba_auth = dask.config.get("coiled.package_sync.conda.cred_sources.mamba", True)
|
|
869
|
+
use_rattler_auth = dask.config.get("coiled.package_sync.conda.cred_sources.rattler", True)
|
|
746
870
|
|
|
747
871
|
no_auth_url = parsed_url._replace(auth=None).url
|
|
748
872
|
auth_parts = (
|
|
@@ -756,10 +880,12 @@ def set_auth_for_url(url: Url | str) -> str:
|
|
|
756
880
|
or (get_conda_auth(no_auth_url) if use_conda_auth else None)
|
|
757
881
|
# mamba could have URL stored by netloc/path or netloc
|
|
758
882
|
or ((get_mamba_auth(f"{netloc}{path}") or get_mamba_auth(netloc)) if use_mamba_auth else None)
|
|
883
|
+
# rattler/pixi stores URL stored by netloc in keyring or a fallback file
|
|
884
|
+
or (get_rattler_auth(netloc) if use_rattler_auth else None)
|
|
759
885
|
)
|
|
760
886
|
if auth_parts is not None:
|
|
761
887
|
username, password = auth_parts
|
|
762
|
-
if username ==
|
|
888
|
+
if username == CONDA_TOKEN_USERNAME:
|
|
763
889
|
# If the username indicates this is a token (which only happens for mamba auth)
|
|
764
890
|
# the token should be embedded directly in the URL and not in the auth portion
|
|
765
891
|
|
|
@@ -775,8 +901,11 @@ def set_auth_for_url(url: Url | str) -> str:
|
|
|
775
901
|
elif username or password:
|
|
776
902
|
parsed_url = parsed_url._replace(auth=f"{username or ''}:{password or ''}")
|
|
777
903
|
|
|
778
|
-
if username and username !=
|
|
779
|
-
|
|
904
|
+
if username and username != CONDA_TOKEN_USERNAME and not password:
|
|
905
|
+
if username == AUTH_BEARER_USERNAME:
|
|
906
|
+
logger.warning(f"No bearer token found for {parsed_url.url}")
|
|
907
|
+
else:
|
|
908
|
+
logger.info(f"No password found for {parsed_url.url}")
|
|
780
909
|
elif not username:
|
|
781
910
|
logger.info(f"No username or password found for {parsed_url.url}")
|
|
782
911
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|