Conversation
…ion and more convenient interactions as part of notebooks by novice python users (geologists). Updated some of the method names to match pandas/tables syntax.
# Conflicts: # packages/evo-widgets/docs/examples/displaying-typed-objects.ipynb # packages/evo-widgets/src/evo/widgets/formatters.py
…jects-variogram # Conflicts: # code-samples/geoscience-objects/README.md # code-samples/geoscience-objects/simplified-object-interactions/simplified-object-interactions.ipynb # packages/evo-objects/docs/examples/typed-objects.ipynb # packages/evo-objects/src/evo/objects/typed/__init__.py # packages/evo-objects/src/evo/objects/typed/base.py # packages/evo-widgets/README.md # packages/evo-widgets/docs/examples/displaying-typed-objects.ipynb # packages/evo-widgets/pyproject.toml # packages/evo-widgets/src/evo/widgets/__init__.py # packages/evo-widgets/src/evo/widgets/formatters.py # packages/evo-widgets/src/evo/widgets/urls.py # packages/evo-widgets/tests/test_formatters.py # packages/evo-widgets/tests/test_urls.py # uv.lock
…ped-objects-block-model # Conflicts: # code-samples/geoscience-objects/running-kriging-compute/running-kriging-compute.ipynb
…jects-block-model # Conflicts: # code-samples/geoscience-objects/running-kriging-compute/running-kriging-compute.ipynb # packages/evo-widgets/src/evo/widgets/__init__.py # packages/evo-widgets/src/evo/widgets/formatters.py # packages/evo-widgets/tests/test_formatters.py
RyanMillerSeequent
left a comment
There was a problem hiding this comment.
Have only looked at packages/evo-objects and everything LGTM there.
| def pyarrow_to_dataframe(table: "pa.Table") -> "pd.DataFrame": | ||
| """Convert a PyArrow Table to a pandas DataFrame. | ||
|
|
||
| :param table: The PyArrow Table to convert. | ||
| :return: A pandas DataFrame. | ||
| :raises ImportError: If pyarrow or pandas is not installed. | ||
| """ | ||
| _check_pyarrow_available() | ||
| _check_pandas_available() | ||
| return table.to_pandas() |
There was a problem hiding this comment.
In general I prefer not to use string-literal annotations. In this case I think either including from __future__ import annotations, or conditionally defining the implementation (consistent with evo.objects.DownloadedObject.download_table(...), for example) would be acceptable.
| if TYPE_CHECKING: | ||
| import pandas as pd | ||
| import pyarrow as pa |
There was a problem hiding this comment.
It looks like pandas and pyarrow are always required by typed block models, so it doesn't make sense to defer annotation here
| def _check_pyarrow_available() -> None: | ||
| """Check if pyarrow is available, raise ImportError with helpful message if not.""" | ||
| try: | ||
| import pyarrow # noqa: F401 | ||
| except ImportError as e: | ||
| raise ImportError( | ||
| "pyarrow is required for this operation. Install it with: pip install evo-blockmodels[pyarrow]" | ||
| ) from e | ||
|
|
||
|
|
||
| def _check_pandas_available() -> None: | ||
| """Check if pandas is available, raise ImportError with helpful message if not.""" | ||
| try: | ||
| import pandas # noqa: F401 | ||
| except ImportError as e: | ||
| raise ImportError( | ||
| "pandas is required for this operation. Install it with: pip install evo-blockmodels[pyarrow]" | ||
| ) from e |
There was a problem hiding this comment.
Similarly, these checks seem redundant
| ) from e | ||
|
|
||
|
|
||
| def pyarrow_to_dataframe(table: "pa.Table") -> "pd.DataFrame": |
There was a problem hiding this comment.
In general I prefer not to use string literal annotations, seeing as from __future__ import annotations was added in python 3.7, and starting with python 3.14, annotations are lazily evaluated by default.
| These types are re-exported from evo.common.typed for backward compatibility. | ||
| """ | ||
|
|
||
| from evo.common.typed import BoundingBox, Point3, Size3d, Size3i |
There was a problem hiding this comment.
Now that types have been refactored into the common module I think it would be worth updating the relevant imports wherever they are used. I don't see any reason to keep this module around
|
|
||
| [project.optional-dependencies] | ||
| pyarrow = ["pyarrow>=19.0.0"] | ||
| pyarrow = ["pyarrow>=19.0.0", "pandas>=2.0.0"] |
There was a problem hiding this comment.
I'm wondering if we should rename this optional dependency group to utils for consistency with evo-objects
| ReportResult, | ||
| ReportSpecificationData, | ||
| ) | ||
| from .types import BoundingBox, Point3, Size3d, Size3i |
There was a problem hiding this comment.
Even though these are now provided by evo.common.typed, I think it is still useful to export them from this namespace for convenience. Perhaps import them from evo.common.typed though
| if TYPE_CHECKING: | ||
| from ..client import BlockModelAPIClient | ||
| from ..endpoints.models import ( | ||
| ReportResult as APIReportResult, | ||
| ) | ||
| from ..endpoints.models import ( | ||
| ReportSpecificationWithJobUrl, | ||
| ReportSpecificationWithLastRunInfo, | ||
| ) |
There was a problem hiding this comment.
In general I think it is cleaner to avoid if TYPE_CHECKING: guards unless they are absolutely necessary.
| if TYPE_CHECKING: | |
| from ..client import BlockModelAPIClient | |
| from ..endpoints.models import ( | |
| ReportResult as APIReportResult, | |
| ) | |
| from ..endpoints.models import ( | |
| ReportSpecificationWithJobUrl, | |
| ReportSpecificationWithLastRunInfo, | |
| ) | |
| from ..client import BlockModelAPIClient | |
| from ..endpoints.models import ReportResult as APIReportResult | |
| from ..endpoints.models import ReportSpecificationWithJobUrl, ReportSpecificationWithLastRunInfo |
There was a problem hiding this comment.
Also, the existing convention for using generated models is to import them as:
| if TYPE_CHECKING: | |
| from ..client import BlockModelAPIClient | |
| from ..endpoints.models import ( | |
| ReportResult as APIReportResult, | |
| ) | |
| from ..endpoints.models import ( | |
| ReportSpecificationWithJobUrl, | |
| ReportSpecificationWithLastRunInfo, | |
| ) | |
| from ..client import BlockModelAPIClient | |
| from ..endpoints import models |
Then reference them like so:
def _from_api_result(cls, result: models.ReportResult) -> ReportResult:
This eliminates the need for aliasing and makes it difficult to accidentally export generated models.
| def _require_blockmodels(operation: str = "This operation") -> None: | ||
| """Raise ImportError if evo-blockmodels is not installed.""" | ||
| if not _BLOCKMODELS_AVAILABLE: | ||
| raise ImportError( | ||
| f"{operation} requires the 'evo-blockmodels' package. Install it with: pip install evo-objects[blockmodels]" | ||
| ) |
There was a problem hiding this comment.
I would guard the method definitions where evo.blockmodels is used, rather than raise an exception.
Do consider whether you expect any of the types in this module to be used without evo-blockmodels installed.
| from .typed import BoundingBox as BoundingBox | ||
| from .typed import Point3 as Point3 | ||
| from .typed import Size3d as Size3d | ||
| from .typed import Size3i as Size3i |
There was a problem hiding this comment.
Why are these aliased?
| from .typed import BoundingBox as BoundingBox | |
| from .typed import Point3 as Point3 | |
| from .typed import Size3d as Size3d | |
| from .typed import Size3i as Size3i | |
| from .typed import BoundingBox, Point3, Size3d, Size3i |
This PR brings block models under the same typed object envolope as recently added objects.
Key parts of this pull requets:
BLock models are now an OPTIONAL dependency for objects. We want to have a single point of entry and block models on evo are not just references from the perspective of the user - they are actual block models that the user expects to interact with. If the user does not install block models, they can interact with block model metadata handling the current dependency in a similar way to pandas and numpy. Evo-sdk user will get the whole package, sneaky developer who only cares about very thin set of changes - can get evo-objects with cut down dependencies.
Block model dependencies were adjusted so that by default it can be used just to parse metadata. Interaction with data is now an optional dependency, this way for basic services that don't care about interacting with evo-data and that user evo-objects there is no extra dependency added.
Block models support dataframe conversions, pretty printing via widgets, report creation, and automated mapping/creation from
object_from_reference/path.I have shared typed definitions in Common, without going as far as adding shared functionality that relies on numpy to avoid new depenency on common.
Block model in objects acts as a wrapper for a fully featured typed objects in evo-block models that can exist by itself.
I have not added any converters to and from block model and 3D grid... but it is not that hard as interfaces are similar though not identical and because the objects are different enough - we do not want to have more shared interfaces at this stage other than common base blocks.
OCTREE and FULLY SUBBLOCKED modes support was left out for now (via typed Objects) - PR is large already
This is a meaninful update, so I have bumped up the versions. If we want to release this, I can write up the changelong now as well, though there is one more PR to come after this for Kriging compute.
Checklist