Skip to content
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
12 changes: 12 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ sorts it by the ``X`` dimension:
metadata = pipeline.metadata
log = pipeline.log

Pass ``timing=True`` when creating a pipeline to include PDAL timing
information in ``pipeline.log`` after execution:

.. code-block:: python

import logging
import pdal

pipeline = pdal.Pipeline(json, loglevel=logging.DEBUG, timing=True)
count = pipeline.execute()
timing_log = pipeline.log

Programmatic Pipeline Construction
................................................................................

Expand Down
4 changes: 2 additions & 2 deletions src/pdal/PyPipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ void CountPointTable::reset()


PipelineExecutor::PipelineExecutor(
std::string const& json, std::vector<std::shared_ptr<Array>> arrays, int level)
std::string const& json, std::vector<std::shared_ptr<Array>> arrays, int level, bool timing)
{
if (level < 0 || level > 8)
throw pdal_error("log level must be between 0 and 8!");

LogPtr log(Log::makeLog("pypipeline", &m_logStream));
LogPtr log(Log::makeLog("pypipeline", &m_logStream, timing));
log->setLevel(static_cast<pdal::LogLevel>(level));
m_manager.setLog(log);

Expand Down
2 changes: 1 addition & 1 deletion src/pdal/PyPipeline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class Array;

class PDAL_EXPORT PipelineExecutor {
public:
PipelineExecutor(std::string const& json, std::vector<std::shared_ptr<Array>> arrays, int level);
PipelineExecutor(std::string const& json, std::vector<std::shared_ptr<Array>> arrays, int level, bool timing);
virtual ~PipelineExecutor() = default;

point_count_t execute(pdal::StringList allowedDims);
Expand Down
3 changes: 2 additions & 1 deletion src/pdal/StreamableExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,11 @@ char *PythonPointTable::getPoint(PointId idx)
StreamableExecutor::StreamableExecutor(std::string const& json,
std::vector<std::shared_ptr<Array>> arrays,
int level,
bool timing,
point_count_t chunkSize,
int prefetch,
pdal::StringList allowedDims)
: PipelineExecutor(json, arrays, level)
: PipelineExecutor(json, arrays, level, timing)
, m_table(chunkSize, prefetch)
, m_exc(nullptr)
{
Expand Down
1 change: 1 addition & 0 deletions src/pdal/StreamableExecutor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class StreamableExecutor : public PipelineExecutor
StreamableExecutor(std::string const& json,
std::vector<std::shared_ptr<Array>> arrays,
int level,
bool timing,
point_count_t chunkSize,
int prefetch,
pdal::StringList allowedDim);
Expand Down
2 changes: 1 addition & 1 deletion src/pdal/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__all__ = ["Pipeline", "Stage", "Reader", "Filter", "Writer", "dimensions", "info"]
__version__ = '3.5.4'
__version__ = '3.5.5'

from . import libpdalpython
from .drivers import inject_pdal_drivers
Expand Down
12 changes: 9 additions & 3 deletions src/pdal/libpdalpython.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ namespace pdal {

std::unique_ptr<PipelineIterator> iterator(int chunk_size, int prefetch, pdal::StringList allowedDims) {
return std::unique_ptr<PipelineIterator>(new PipelineIterator(
getJson(), _inputs, _loglevel, chunk_size, prefetch, allowedDims
getJson(), _inputs, _loglevel, _timing, chunk_size, prefetch, allowedDims
));
}

Expand Down Expand Up @@ -214,6 +214,10 @@ namespace pdal {

void setLogLevel(int level) { _loglevel = level; delExecutor(); }

bool getTiming() { return _timing; }

void setTiming(bool timing) { _timing = timing; delExecutor(); }

std::string getLog() { return getExecutor()->getLog(); }

std::string getPipeline() { return getExecutor()->getPipeline(); }
Expand Down Expand Up @@ -291,14 +295,15 @@ namespace pdal {
// does for all of the other methods it knows about
py::gil_scoped_acquire acquire;
if (!_executor)
_executor.reset(new PipelineExecutor(getJson(), _inputs, _loglevel));
_executor.reset(new PipelineExecutor(getJson(), _inputs, _loglevel, _timing));
return _executor.get();
}

private:
std::unique_ptr<PipelineExecutor> _executor;
std::vector<std::shared_ptr<pdal::python::Array>> _inputs;
int _loglevel;
int _loglevel = 0;
bool _timing = false;
};


Expand All @@ -324,6 +329,7 @@ namespace pdal {
.def("iterator", &Pipeline::iterator, "chunk_size"_a=10000, "prefetch"_a=0, py::arg("allowed_dims") =py::list())
.def_property("inputs", nullptr, &Pipeline::setInputs)
.def_property("loglevel", &Pipeline::getLoglevel, &Pipeline::setLogLevel)
.def_property("timing", &Pipeline::getTiming, &Pipeline::setTiming)
.def_property_readonly("log", &Pipeline::getLog)
.def_property_readonly("schema", &Pipeline::getSchema)
.def_property_readonly("srswkt2", &Pipeline::getSrsWKT2)
Expand Down
22 changes: 19 additions & 3 deletions src/pdal/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ def __init__(
json: Optional[str] = None,
dataframes: Sequence[DataFrame] = (),
stream_handlers: Sequence[Callable[[], int]] = (),
*,
timing: bool = False,
):

if json:
Expand All @@ -75,6 +77,7 @@ def __init__(
self.inputs = [(a, None) for a in arrays]

self.loglevel = loglevel
self.timing = timing

def __getstate__(self):
state = self.pipeline
Expand Down Expand Up @@ -104,6 +107,14 @@ def loglevel(self, value: int) -> None:
# super() property setter is not supported
libpdalpython.Pipeline.loglevel.__set__(self, loglevel)

@property
def timing(self) -> bool:
return super().timing

@timing.setter
def timing(self, value: bool) -> None:
libpdalpython.Pipeline.timing.__set__(self, bool(value))

def __ior__(self, other: Union[Stage, Pipeline]) -> Pipeline:
if isinstance(other, Stage):
self._stages.append(other)
Expand All @@ -124,7 +135,7 @@ def __or__(self, other: Union[Stage, Pipeline]) -> Pipeline:
return new

def __copy__(self) -> Pipeline:
clone = self.__class__(loglevel=self.loglevel)
clone = self.__class__(loglevel=self.loglevel, timing=self.timing)
clone._copy_inputs(self)
clone |= self
return clone
Expand Down Expand Up @@ -214,8 +225,13 @@ def inputs(self) -> List[Union[Stage, str]]:
def options(self) -> Dict[str, Any]:
return dict(self._options)

def pipeline(self, *arrays: np.ndarray, loglevel: int = logging.ERROR) -> Pipeline:
return Pipeline((self,), arrays, loglevel)
def pipeline(
self,
*arrays: np.ndarray,
loglevel: int = logging.ERROR,
timing: bool = False,
) -> Pipeline:
return Pipeline((self,), arrays, loglevel=loglevel, timing=timing)

def __or__(self, other: Union[Stage, Pipeline]) -> Pipeline:
return Pipeline((self, other))
Expand Down
29 changes: 28 additions & 1 deletion test/test_pipeline.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import logging
import os
import re
import sys

from itertools import product
Expand Down Expand Up @@ -53,6 +54,21 @@ def test_construction(self, filename):
assert isinstance(p, pdal.Pipeline)
assert len(p.stages) == 2

def test_timing_is_optional_public_api(self):
with open(os.path.join(DATADIRECTORY, "chip.json"), "r") as f:
pipeline_json = f.read()

p = pdal.Pipeline(None, (), logging.ERROR, pipeline_json)
assert p.timing is False
assert p.execute() == 1065

with pytest.raises(TypeError):
pdal.Pipeline(None, (), logging.ERROR, pipeline_json, (), (), True)

reader = pdal.Reader(os.path.join(DATADIRECTORY, "1.2-with-color.las"))
assert reader.pipeline().timing is False
assert reader.pipeline(timing=True).timing is True

@pytest.mark.parametrize(
"pipeline",
[
Expand Down Expand Up @@ -338,6 +354,7 @@ def test_logging(self, filename):
"""Can we fetch log output"""
r = get_pipeline(filename)
assert r.loglevel == logging.ERROR
assert r.timing is False
assert r.log == ""

for loglevel in logging.CRITICAL, -1:
Expand All @@ -356,6 +373,17 @@ def test_logging(self, filename):
assert "(pypipeline Debug) Executing pipeline in standard mode" in r.log
assert "(pypipeline writers.las Debug)" in r.log

def test_logging_timing(self):
"""Can we fetch log output decorated with timing information"""
with open(os.path.join(DATADIRECTORY, "chip.json"), "r") as f:
r = pdal.Pipeline(f.read(), loglevel=logging.DEBUG, timing=True)

assert r.timing is True
count = r.execute()
assert count == 1065
assert re.search(r"\(pypipeline readers\.las Debug [0-9.]+\)", r.log)
assert re.search(r"\(pypipeline Debug [0-9.]+\) Executing pipeline in standard mode", r.log)

@pytest.mark.skipif(
not hasattr(pdal.Filter, "python"),
reason="filters.python PDAL plugin is not available",
Expand Down Expand Up @@ -904,4 +932,3 @@ def invalid_stream_handler():
with pytest.raises(RuntimeError,
match=f"Stream chunk size not in the range of array length: {invalid_chunk_size}"):
p.execute()

Loading