Skip to content

Commit 37e71da

Browse files
committed
make and test reusable function in pvlib.tools
1 parent 1df8af3 commit 37e71da

File tree

3 files changed

+59
-9
lines changed

3 files changed

+59
-9
lines changed

pvlib/iotools/psm4.py

+2-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import requests
1212
import pandas as pd
1313
from json import JSONDecodeError
14-
import contextlib
14+
from pvlib import tools
1515

1616
NSRDB_API_BASE = "https://developer.nrel.gov/api/nsrdb/v2/solar/"
1717
PSM4_AGG_ENDPOINT = "nsrdb-GOES-aggregated-v4-0-0-download.csv"
@@ -735,14 +735,7 @@ def read_nsrdb_psm4(filename, map_variables=True):
735735
.. [2] `Standard Time Series Data File Format
736736
<https://web.archive.org/web/20170207203107/https://sam.nrel.gov/sites/default/files/content/documents/pdf/wfcsv.pdf>`_
737737
"""
738-
if hasattr(filename, "read"):
739-
# already a file-like object
740-
context = contextlib.nullcontext(filename)
741-
else:
742-
# otherwise, assume a filename or path
743-
context = open(str(filename), 'r')
744-
745-
with context as fbuf:
738+
with tools._file_context_manager(filename) as fbuf:
746739
# The first 2 lines of the response are headers with metadata
747740
metadata_fields = fbuf.readline().split(',')
748741
metadata_values = fbuf.readline().split(',')

pvlib/tools.py

+27
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Collection of functions used in pvlib_python
33
"""
44

5+
import contextlib
56
import datetime as dt
67
import warnings
78

@@ -559,3 +560,29 @@ def normalize_max2one(a):
559560
except ValueError: # fails for pandas objects
560561
res = a.div(a.abs().max(axis=0, skipna=True))
561562
return res
563+
564+
565+
def _file_context_manager(filename_or_object, mode='r'):
566+
"""
567+
Open a filename/path for reading, or pass a file-like object
568+
through unchanged.
569+
570+
Parameters
571+
----------
572+
filename_or_object : str, path-like, or file-like object
573+
The filename/path or object to convert to an object
574+
575+
Returns
576+
-------
577+
context : context manager
578+
A file-like object to be used via python's "with [context] as buffer:"
579+
syntax.
580+
"""
581+
582+
if hasattr(filename_or_object, "read"):
583+
# already a file-like object
584+
context = contextlib.nullcontext(filename_or_object)
585+
else:
586+
# otherwise, assume a filename or path
587+
context = open(str(filename_or_object), mode=mode)
588+
return context

tests/test_tools.py

+30
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
from datetime import datetime
2+
from io import StringIO
3+
import os
4+
from pathlib import Path
5+
import tempfile
26
from zoneinfo import ZoneInfo
37

48
import numpy as np
@@ -252,3 +256,29 @@ def test_djd_to_datetime():
252256

253257
expected = datetime(1974, 6, 22, 23, 30, 15, tzinfo=ZoneInfo("UTC"))
254258
assert tools.djd_to_datetime(djd) == expected
259+
260+
261+
def test__file_context_manager():
262+
with tempfile.TemporaryDirectory() as td:
263+
# make a test file
264+
filename = os.path.join(td, 'test.txt')
265+
with open(filename, 'w') as fh:
266+
fh.write('test content')
267+
268+
# test with filename as string:
269+
with tools._file_context_manager(filename) as obj:
270+
assert obj.read() == "test content"
271+
272+
# test with filename as Path:
273+
with tools._file_context_manager(Path(filename)) as obj:
274+
assert obj.read() == "test content"
275+
276+
# test with file object:
277+
with open(filename, "r") as f:
278+
with tools._file_context_manager(f) as obj:
279+
assert obj.read() == "test content"
280+
281+
# test with buffer:
282+
buffer = StringIO("test content")
283+
with tools._file_context_manager(buffer) as obj:
284+
assert obj.read() == "test content"

0 commit comments

Comments
 (0)