Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix upload file to USS and integration tests #248

Merged
merged 2 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"recommendations": [
"ms-python.black-formatter",
"ms-python.isort",
"ms-python.vscode-pylance"
]
}
12 changes: 6 additions & 6 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"editor.formatOnSave": true,
"python.formatting.provider": "black",
"rust-analyzer.linkedProjects": ["./src/secrets/Cargo.toml"],
"[python]": {
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
"source.organizeImports": "explicit"
},
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true
},
"python.testing.pytestArgs": ["tests"],
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
"rust-analyzer.linkedProjects": ["./src/secrets/Cargo.toml"],
}
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

All notable changes to the Zowe Client Python SDK will be documented in this file.

## Recent Changes

### Bug Fixes

- Fixed `Files.download_dsn` and `Files.download_binary_dsn` failing to write contents to disk [#179](https://github.com/zowe/zowe-client-python-sdk/issues/179)
- Fixed `Files.delete_data_set` and `Files.list_dsn_members` so they encode URLs correctly
- Fixed `Files.upload_to_uss` displaying an unclosed file warning
- Fixed loading environment variables when there is no schema file in current directory

### Enhancements

- Added method `Files.download_uss` to download USS files to disk
- Added support to `Tso` class for loading TSO profile properties

## `1.0.0-dev13`

### Bug Fixes
Expand Down
8 changes: 3 additions & 5 deletions src/core/zowe/core_for_zowe_sdk/config_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,11 +152,9 @@ def validate_schema(self) -> None:

# validate the $schema property
if path_schema_json:
validate_config_json(self.jsonc, path_schema_json, cwd = self.location)
validate_config_json(self.jsonc, path_schema_json, cwd=self.location)

def schema_list(
self,
) -> list:
def schema_list(self, cwd=None) -> list:
"""
Loads the schema properties
in a sorted order according to the priority
Expand All @@ -180,7 +178,7 @@ def schema_list(
schema_json = json.load(f)

elif not os.path.isabs(schema):
schema = os.path.join(self.location, schema)
schema = os.path.join(self.location or cwd, schema)
with open(schema) as f:
schema_json = json.load(f)
else:
Expand Down
10 changes: 5 additions & 5 deletions src/core/zowe/core_for_zowe_sdk/profile_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,7 @@ def config_filepath(self) -> Optional[str]:
return self.project_config.filepath

@staticmethod
def get_env(
cfg: ConfigFile,
) -> dict:
def get_env(cfg: ConfigFile, cwd=None) -> dict:
"""
Maps the env variables to the profile properties

Expand All @@ -127,7 +125,7 @@ def get_env(
Containing profile properties from env variables (prop: value)
"""

props = cfg.schema_list()
props = cfg.schema_list(cwd)
if props == []:
return {}

Expand Down Expand Up @@ -255,6 +253,7 @@ def load(
profiles_merged: dict = {}
cfg_name = None
cfg_schema = None
cfg_schema_dir = None

for cfg_layer in (self.project_user_config, self.project_config, self.global_user_config, self.global_config):
if cfg_layer.profiles is None:
Expand All @@ -272,6 +271,7 @@ def load(
cfg_name = cfg_layer.name
if not cfg_schema and cfg_layer.schema_property:
cfg_schema = cfg_layer.schema_property
cfg_schema_dir = cfg_layer._location

usrProject = self.project_user_config.profiles or {}
project = self.project_config.profiles or {}
Expand Down Expand Up @@ -299,7 +299,7 @@ def load(
missing_secure_props.extend(profile_loaded.missing_secure_props)

if override_with_env:
env_var = {**self.get_env(cfg)}
env_var = {**self.get_env(cfg, cfg_schema_dir)}

if profile_type != BASE_PROFILE:
profile_props = {
Expand Down
81 changes: 47 additions & 34 deletions src/zos_files/zowe/zos_files_for_zowe_sdk/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@


import os
import shutil

from zowe.core_for_zowe_sdk import SdkApi
from zowe.core_for_zowe_sdk.exceptions import FileNotFound
Expand Down Expand Up @@ -140,12 +139,8 @@
additional_parms["start"] = member_start
if member_pattern is not None:
additional_parms["pattern"] = member_pattern
url = "{}ds/{}/member".format(self.request_endpoint, dataset_name)
separator = "?"
for k, v in additional_parms.items():
url = "{}{}{}={}".format(url, separator, k, v)
separator = "&"
custom_args["url"] = self._encode_uri_component(url)
custom_args["params"] = additional_parms
custom_args["url"] = "{}ds/{}/member".format(self.request_endpoint, self._encode_uri_component(dataset_name))

Check warning on line 143 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L142-L143

Added lines #L142 - L143 were not covered by tests
custom_args["headers"]["X-IBM-Max-Items"] = "{}".format(limit)
custom_args["headers"]["X-IBM-Attributes"] = attributes
response_json = self.request_handler.perform_request("GET", custom_args)
Expand Down Expand Up @@ -443,13 +438,13 @@

Returns
-------
raw
A raw socket response
response
A response object from the requests library
"""
custom_args = self._create_custom_request_arguments()
custom_args["url"] = "{}ds/{}".format(self.request_endpoint, self._encode_uri_component(dataset_name))
raw_response = self.request_handler.perform_streamed_request("GET", custom_args)
return raw_response
response = self.request_handler.perform_streamed_request("GET", custom_args)
return response

Check warning on line 447 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L446-L447

Added lines #L446 - L447 were not covered by tests

def get_dsn_binary_content(self, dataset_name, with_prefixes=False):
"""
Expand All @@ -462,8 +457,8 @@
default: False
Returns
-------
bytes
The contents of the dataset with no transformation
response
A response object from the requests library
"""
custom_args = self._create_custom_request_arguments()
custom_args["url"] = "{}ds/{}".format(self.request_endpoint, self._encode_uri_component(dataset_name))
Expand All @@ -472,8 +467,8 @@
custom_args["headers"]["X-IBM-Data-Type"] = "record"
else:
custom_args["headers"]["X-IBM-Data-Type"] = "binary"
content = self.request_handler.perform_request("GET", custom_args)
return content
response = self.request_handler.perform_request("GET", custom_args)
return response

Check warning on line 471 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L470-L471

Added lines #L470 - L471 were not covered by tests

def get_dsn_binary_content_streamed(self, dataset_name, with_prefixes=False):
"""
Expand All @@ -486,8 +481,8 @@
default: False
Returns
-------
raw
The raw socket response
response
A response object from the requests library
"""
custom_args = self._create_custom_request_arguments()
custom_args["url"] = "{}ds/{}".format(self.request_endpoint, self._encode_uri_component(dataset_name))
Expand All @@ -496,8 +491,8 @@
custom_args["headers"]["X-IBM-Data-Type"] = "record"
else:
custom_args["headers"]["X-IBM-Data-Type"] = "binary"
content = self.request_handler.perform_streamed_request("GET", custom_args)
return content
response = self.request_handler.perform_streamed_request("GET", custom_args)
return response

Check warning on line 495 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L494-L495

Added lines #L494 - L495 were not covered by tests

def write_to_dsn(self, dataset_name, data, encoding=_ZOWE_FILES_DEFAULT_ENCODING):
"""Write content to an existing dataset.
Expand All @@ -516,9 +511,10 @@

def download_dsn(self, dataset_name, output_file):
"""Retrieve the contents of a dataset and saves it to a given file."""
raw_response = self.get_dsn_content_streamed(dataset_name)
response = self.get_dsn_content_streamed(dataset_name)

Check warning on line 514 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L514

Added line #L514 was not covered by tests
with open(output_file, "w", encoding="utf-8") as f:
shutil.copyfileobj(raw_response, f)
for chunk in response.iter_content(chunk_size=4096, decode_unicode=True):
f.write(chunk)

Check warning on line 517 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L516-L517

Added lines #L516 - L517 were not covered by tests

def download_binary_dsn(self, dataset_name, output_file, with_prefixes=False):
"""Retrieve the contents of a binary dataset and saves it to a given file.
Expand All @@ -529,15 +525,11 @@
output_file:str - Name of the local file to create
with_prefixes:boolean - If true, include a four big endian bytes record length prefix.
The default is False

Returns
-------
bytes
Binary content of the dataset.
"""
content = self.get_dsn_binary_content_streamed(dataset_name, with_prefixes=with_prefixes)
response = self.get_dsn_binary_content_streamed(dataset_name, with_prefixes=with_prefixes)

Check warning on line 529 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L529

Added line #L529 was not covered by tests
with open(output_file, "wb") as f:
shutil.copyfileobj(content, f)
for chunk in response.iter_content(chunk_size=4096):
f.write(chunk)

Check warning on line 532 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L531-L532

Added lines #L531 - L532 were not covered by tests

def upload_file_to_dsn(self, input_file, dataset_name, encoding=_ZOWE_FILES_DEFAULT_ENCODING):
"""Upload contents of a given file and uploads it to a dataset."""
Expand All @@ -564,21 +556,42 @@
def upload_file_to_uss(self, input_file, filepath_name, encoding=_ZOWE_FILES_DEFAULT_ENCODING):
"""Upload contents of a given file and uploads it to UNIX file"""
if os.path.isfile(input_file):
in_file = open(input_file, "r", encoding="utf-8")
file_contents = in_file.read()
response_json = self.write_to_uss(filepath_name, file_contents)
with open(input_file, "r", encoding="utf-8") as in_file:
response_json = self.write_to_uss(filepath_name, in_file)

Check warning on line 560 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L559-L560

Added lines #L559 - L560 were not covered by tests
else:
raise FileNotFound(input_file)

def get_file_content_streamed(self, file_path, binary=False):
"""Retrieve the contents of a given USS file streamed.

Returns
-------
response
A response object from the requests library
"""
custom_args = self._create_custom_request_arguments()
custom_args["url"] = "{}fs/{}".format(self.request_endpoint, self._encode_uri_component(file_path.lstrip("/")))
if binary:
custom_args["headers"]["X-IBM-Data-Type"] = "binary"
response = self.request_handler.perform_streamed_request("GET", custom_args)
return response

Check warning on line 577 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L572-L577

Added lines #L572 - L577 were not covered by tests

def download_uss(self, file_path, output_file, binary=False):
"""Retrieve the contents of a USS file and saves it to a local file."""
response = self.get_file_content_streamed(file_path, binary)
with open(output_file, "wb" if binary else "w", encoding="utf-8") as f:
for chunk in response.iter_content(chunk_size=4096, decode_unicode=not binary):
f.write(chunk)

Check warning on line 584 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L581-L584

Added lines #L581 - L584 were not covered by tests

def delete_data_set(self, dataset_name, volume=None, member_name=None):
"""Deletes a sequential or partitioned data."""
custom_args = self._create_custom_request_arguments()
if member_name is not None:
dataset_name = f"{dataset_name}({member_name})"
url = "{}ds/{}".format(self.request_endpoint, dataset_name)
url = "{}ds/{}".format(self.request_endpoint, self._encode_uri_component(dataset_name))

Check warning on line 591 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L591

Added line #L591 was not covered by tests
if volume is not None:
url = "{}ds/-{}/{}".format(self.request_endpoint, volume, dataset_name)
custom_args["url"] = self._encode_uri_component(url)
url = "{}ds/-{}/{}".format(self.request_endpoint, volume, self._encode_uri_component(dataset_name))
custom_args["url"] = url

Check warning on line 594 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L593-L594

Added lines #L593 - L594 were not covered by tests
response_json = self.request_handler.perform_request("DELETE", custom_args, expected_code=[200, 202, 204])
return response_json

Expand Down
57 changes: 27 additions & 30 deletions src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,8 @@
A JSON containing the result of the request execution
"""
if os.path.isfile(jcl_path):
jcl_file = open(jcl_path, "r", encoding="utf-8")
file_content = jcl_file.read()
jcl_file.close()
with open(jcl_path, "r", encoding="utf-8") as jcl_file:
file_content = jcl_file.read()

Check warning on line 271 in src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py

View check run for this annotation

Codecov / codecov/patch

src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py#L270-L271

Added lines #L270 - L271 were not covered by tests
return self.submit_plaintext(file_content)
else:
raise FileNotFoundError("Provided argument is not a file path {}".format(jcl_path))
Expand Down Expand Up @@ -388,32 +387,30 @@
A JSON containing the result of the request execution
"""

_job_name = status["jobname"]
_job_id = status["jobid"]
_job_correlator = status["job-correlator"]

_output_dir = os.path.join(output_dir, _job_name, _job_id)
os.makedirs(_output_dir, exist_ok=True)
_output_file = os.path.join(output_dir, _job_name, _job_id, "jcl.txt")
_data_spool_file = self.get_jcl_text(_job_correlator)
_dataset_content = _data_spool_file["response"]
_out_file = open(_output_file, "w", encoding="utf-8")
_out_file.write(_dataset_content)
_out_file.close()

_spool = self.get_spool_files(_job_correlator)
for _spool_file in _spool:
_stepname = _spool_file["stepname"]
_ddname = _spool_file["ddname"]
_spoolfile_id = _spool_file["id"]
_output_dir = os.path.join(output_dir, _job_name, _job_id, _stepname)
os.makedirs(_output_dir, exist_ok=True)

_output_file = os.path.join(output_dir, _job_name, _job_id, _stepname, _ddname)
_data_spool_file = self.get_spool_file_contents(_job_correlator, _spoolfile_id)
_dataset_content = _data_spool_file["response"]
_out_file = open(_output_file, "w", encoding="utf-8")
_out_file.write(_dataset_content)
_out_file.close()
job_name = status["jobname"]
job_id = status["jobid"]
job_correlator = status["job-correlator"]

Check warning on line 392 in src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py

View check run for this annotation

Codecov / codecov/patch

src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py#L390-L392

Added lines #L390 - L392 were not covered by tests

output_dir = os.path.join(output_dir, job_name, job_id)
os.makedirs(output_dir, exist_ok=True)
output_file = os.path.join(output_dir, job_name, job_id, "jcl.txt")
data_spool_file = self.get_jcl_text(job_correlator)
dataset_content = data_spool_file["response"]
with open(output_file, "w", encoding="utf-8") as out_file:
out_file.write(dataset_content)

Check warning on line 400 in src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py

View check run for this annotation

Codecov / codecov/patch

src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py#L394-L400

Added lines #L394 - L400 were not covered by tests

spool = self.get_spool_files(job_correlator)
for spool_file in spool:
stepname = spool_file["stepname"]
ddname = spool_file["ddname"]
spoolfile_id = spool_file["id"]
output_dir = os.path.join(output_dir, job_name, job_id, stepname)
os.makedirs(output_dir, exist_ok=True)

Check warning on line 408 in src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py

View check run for this annotation

Codecov / codecov/patch

src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py#L402-L408

Added lines #L402 - L408 were not covered by tests

output_file = os.path.join(output_dir, job_name, job_id, stepname, ddname)
data_spool_file = self.get_spool_file_contents(job_correlator, spoolfile_id)
dataset_content = data_spool_file["response"]
with open(output_file, "w", encoding="utf-8") as out_file:
out_file.write(dataset_content)

Check warning on line 414 in src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py

View check run for this annotation

Codecov / codecov/patch

src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py#L410-L414

Added lines #L410 - L414 were not covered by tests

return
Loading