-
Notifications
You must be signed in to change notification settings - Fork 35
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
Add Genertobs #704
Open
daniel-sol
wants to merge
281
commits into
equinor:main
Choose a base branch
from
daniel-sol:genertobs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add Genertobs #704
Changes from all commits
Commits
Show all changes
281 commits
Select commit
Hold shift + click to select a range
87742fb
Change name of data object
daniel-sol 589dec6
Add test for extract_from_row
daniel-sol 7a8b9b4
Correct typo in input csv
daniel-sol 06b466b
Move removal of duplicates
daniel-sol 066a085
Add more to tests for genertobs
daniel-sol a92eea5
Add tests for extraction functions for genertobs
daniel-sol fb2d42e
Use fmuobs function to convert to dict
daniel-sol b6e9264
Add utility functions for tests genertobs
daniel-sol af56dfd
Use utility function in test
daniel-sol 517502f
Add conversion to specific date format
daniel-sol 4f6b91a
Convert function to returning dictionary
daniel-sol df37e1c
Add function for reading yaml config
daniel-sol 37276cc
Add validation of config
daniel-sol d7c201b
Add logging message
daniel-sol 348e177
Swap from returning records to list
daniel-sol 536304e
Swap input type to function
daniel-sol 6a05e38
Adapt to using dictionary not pd.series
daniel-sol d75e67c
Elaborate log message
daniel-sol 4d4745b
Add more validation of config
daniel-sol 4c0495a
Add function for reading all data from yaml config
daniel-sol c1d8863
Create test data to align with test_config
daniel-sol a07944d
Adapt tests to dict output
daniel-sol c6d0e47
Backtrack on using dict instead of Dataframe
daniel-sol 015f889
Align tests with lates commit
daniel-sol e661659
Add more logging
daniel-sol d0418dc
Correct function
daniel-sol 03efd16
Add test data for function convert_obs_df_to_dict
daniel-sol 99413cf
Reintroduce function for conversion to all caps for column names
daniel-sol d4b847c
Add function for converting observation frame
daniel-sol 03580c7
Tidy testdata
daniel-sol 7140ba9
Add output column when extracting rfts
daniel-sol 3bc4308
Remove writing of data in test
daniel-sol f27ce2f
Introduce deepcopy
daniel-sol 61369ef
Introduce more logging to solve anchor issue
daniel-sol ec52251
Remove setting of dummy value
daniel-sol e2b7a85
Unite tests for genertobs_unstable
daniel-sol 90f0f10
Add TODO label
daniel-sol 507383c
Prepare tests for error handling
daniel-sol e609ab0
Add function for error handling
daniel-sol 1f5493b
Complete first version of function
daniel-sol 460a762
Complete test for add or modify error function
daniel-sol bcd20cb
Add docstring
daniel-sol 94aad51
Ensure error is str at function start
daniel-sol a19fb52
Apply function
daniel-sol ad1937f
Adapt tests to recent changes
daniel-sol 6b024ac
Introduce some columns that have to be list
daniel-sol 4b7b032
Refactor function to return list
daniel-sol d86a548
Give genertobs test config better name
daniel-sol 2ade06c
Tidy assignment of class name
daniel-sol 8c6441e
Add expected results as fixture
daniel-sol 36b9493
Mature test for generating data from config
daniel-sol f6b16e3
Add forgotten file
daniel-sol ec7388a
Remove class assignment from dict
daniel-sol 105b865
Add function for export with fmu.dataio
daniel-sol 9efb207
Add TODO task
daniel-sol 8b29252
Add ert obs file to compare to
daniel-sol efd0df3
Add _utilities module and restructure
daniel-sol dbe026a
Import forgotten function
daniel-sol 7842995
Rename function
daniel-sol f28fd55
Rename module
daniel-sol 697086c
Apply change of module
daniel-sol 8738ec5
Import Path
daniel-sol 0db0d98
Add functions for writing in ertobs format
daniel-sol 1d00265
Add trajectory files for tests
daniel-sol 6c91db2
Add writing of gendata_rft str
daniel-sol de0c3e6
Add missing docstring
daniel-sol ca4417e
Tidy
daniel-sol d2e72b8
Move write functions to separate module
daniel-sol 77dfcb2
Remove extraction of both dataframe and dict in same functions
daniel-sol d799dfc
Remove unused function
daniel-sol daf5d2a
Align tests and test data with latest changes
daniel-sol d107506
Add parent path
daniel-sol 9b403cb
Make export with fmudataio work
daniel-sol f300f0f
Add main module
daniel-sol b08ae42
Add Snorre test data for testing
daniel-sol 980913a
Move export with dataio to _writers
daniel-sol fd067c1
Move string generation to log string
daniel-sol 0394803
Add newline
daniel-sol 0fcface
Fix parent path and writing of files
daniel-sol 9f21f38
Add run function to main
daniel-sol e723b36
Tidy parse_config
daniel-sol 06d7c02
Remove file
daniel-sol 220ac8c
Move config to more realistic folder
daniel-sol b3a38bc
Implement correct deletion of folder
daniel-sol 6b20a36
Update tests
daniel-sol 8ba1805
Rename variable
daniel-sol b59e9fb
Switch to writing only file name
daniel-sol e138542
Add typing
daniel-sol 57d6ed3
Update tests
daniel-sol 63b7b80
Fix pandas copy issue
daniel-sol 4a81536
Remove function for making upper caps list
daniel-sol e6a58cd
Add separate functions per content
daniel-sol 6989d6f
Fix and add to convert functions
daniel-sol b36dc89
Split into keeper columns and additionals
daniel-sol ff024ee
Simplify and remove redundant functions
daniel-sol d341485
Move to writing from dataframes rather than dictionariaries
daniel-sol 8a15d6c
Ensure value col is numeric
daniel-sol 01a27a1
Change fixture to read from pickled results
daniel-sol 676163a
Modify test data to improve testing
daniel-sol 6da8a5d
Update tests
daniel-sol 7cc3abb
Implement truncation rules when error is in %
daniel-sol 9fac36a
Add function for ensuring value and error is numeric
daniel-sol 0e7d92a
Correct typo
daniel-sol 32ac6cb
Add functionality for inactivation
daniel-sol c2f95eb
Add genertobs as script
daniel-sol fd60445
Add renaming rule
daniel-sol b43fd0b
Add docstring
daniel-sol bcfe22a
Add export of prepocessed for sumo
daniel-sol b4ae0fa
Split generation of prefix out as function for reuse
daniel-sol 6930fc4
Rename argument
dbsequ 31fa078
Rephrase help message
dbsequ d45a3eb
Add timestamp message in all files
daniel-sol c149e0d
Add writing of gendata_rft input
daniel-sol ac17579
Add functionality for removing unwanted characters and rows
daniel-sol 51ce85c
Change indentation
daniel-sol f24c851
Add missing timestamping
daniel-sol bea4ba6
Remove _
daniel-sol 4fb0e99
Add logging message
daniel-sol 3de5bbb
Set is_observation for all exports to sumo
daniel-sol 6ad0f45
Complete debug mode
daniel-sol b311eec
Modify test data
daniel-sol da11087
Update tests
daniel-sol c1f96c8
Move Snorre well alias file
daniel-sol f18f9ee
Add to undefined values
daniel-sol c58c1fa
Modify removal of undefs, add removal of whitespace
daniel-sol a4bbc07
Modify function
daniel-sol ff4044d
Move check and fix function to writing files
daniel-sol 01da157
Correct the handling of undefined values
daniel-sol b0b7097
Fix with backdoor solution
daniel-sol 3f720a0
Add missing fmu config file
daniel-sol b92c2a8
Correct duplicate fixture function name
daniel-sol f29b36f
Add pickled data results for use as fixture
daniel-sol 529d5fa
Add logging message
daniel-sol 1244b39
Fix wrong call to test
daniel-sol 0b7a9e4
Add dependencies
daniel-sol a3a0a04
Add missing text
daniel-sol dbdd279
Add setting of comment sign
daniel-sol c5d907b
Ensure writing of rft's to folder
daniel-sol 82fe2ce
Improve warning display
daniel-sol ef09460
Add chevron brackets
daniel-sol 38b8aeb
Move export of preprocessed to pyarrow
daniel-sol 240560b
Allow folder to exist
daniel-sol 9a1a0c8
Add generation of include for upload to sumo
daniel-sol f8fea0a
Add module for upload to sumo
daniel-sol 42d452a
Add integration test
daniel-sol 3cce4bb
Add creation of folder (Again..)
daniel-sol 5986319
Change logging level
daniel-sol 8c012ef
Change indent to fix
daniel-sol e736044
Fix main
daniel-sol 13e99c2
Edit tests
daniel-sol 357ae46
Move to conventional generation of metdata
daniel-sol 65934c9
Add preprocessed data to test data
daniel-sol d5d1449
Add upload obs to sumo as script
daniel-sol fe29c20
Remove unneccesary imports
daniel-sol 2afeeed
Make upload of obs to sumo work
daniel-sol 41c2252
Move to f-string in exception
daniel-sol c54b5ec
Fix paths for sumo file
daniel-sol 3dac27b
Fix scratch string
daniel-sol accc566
Change variable name to more descriptive name
daniel-sol 436a6b5
Add timestamp to ert jobs
daniel-sol 6ebbffb
Update test setup
daniel-sol 9c5bc78
Add writing of sumo_env to hook
daniel-sol b6b5f1d
Modify test
daniel-sol d86ed58
Fix export to sumo folder
daniel-sol 06b9b5d
Tidy folder setting and printing
daniel-sol 8bc5638
Add return variable
daniel-sol d9b7aa1
Update tests after changes
daniel-sol 38d868e
Move inactivation to writing of ert input
daniel-sol c129b77
Fix function and make it return results
daniel-sol b24d869
Isort
daniel-sol 24cd14a
Resolve absolute path when generating file
daniel-sol 9a203cd
Fix logger bugs
daniel-sol 60405fd
Allow for installation of wf jobs, not just forward models
daniel-sol 9a41ef4
Remove timestamp in rft obs files
daniel-sol 5c4ddab
Remove prefix for rft position file
daniel-sol 2a84636
Mypy
daniel-sol c1f05fe
Add FileNotFoundError
daniel-sol 1123d80
More mypy and Isort
daniel-sol 5655444
Black and ruff
daniel-sol b47690e
Ensure rft outfolder is assigned throughout
daniel-sol 6d82d65
Add warning
daniel-sol cb8b7aa
Allow for comments in input files
daniel-sol 5dadfd7
Remove - as unwanted character
daniel-sol 0ccaa1c
Add activation and alias file from config file
daniel-sol a201d70
Add renaming via alias file
daniel-sol bdbb824
Add more valid keyword arguments to validation
daniel-sol 8405d15
Change type in call
daniel-sol 43f2495
Add Snorre test
daniel-sol 5fc4240
Update tests to be in line with recent changes
daniel-sol d0dafa6
Isort
daniel-sol 80cda35
Add fixes suggested by ruff
daniel-sol ebfea9a
Add neccessary files to make test run
daniel-sol c063150
Add skipping of upload to sumo when running with github actions
daniel-sol 2335c2e
Add fmu-sumo-uploader as dependency
daniel-sol f2a7401
Make tests for config jobs more flexible
daniel-sol 6c8b3a3
Change way of extracting user
daniel-sol 0bebd09
Move check if github actions to conftest
daniel-sol 5211e74
Add explanatory text snippets for include file
daniel-sol 23b204e
Add line break
daniel-sol 1da6f8d
Remove preparation of data for sumo and related dependencies
daniel-sol dceaa2f
Rename to default_error in input config
daniel-sol 25fd65a
Remove fmu_config as input
daniel-sol 7f93207
Move to folder name from config file name
daniel-sol 1d923cd
Add module for data validation
daniel-sol eca9fc4
Add optional error fields to config
daniel-sol 5443edd
Add dunder methods to root class
daniel-sol ff5bf0e
Update descriptions
daniel-sol 437aa98
Add field validator for observation path
daniel-sol a35d715
Add validation for when default_error is string
daniel-sol ebdaee4
Add validation of error limits
daniel-sol f3da8d7
Add return value in custom field_validators
daniel-sol 7e639aa
Add ValueError when default_error is negative
daniel-sol 471277c
Add assert config element remains the same
daniel-sol 13834a6
Add warning
daniel-sol ed94a50
Tidy
daniel-sol 86b5852
Move type timeseries to summary, remove hack validation
daniel-sol 3b950b4
Add RftConfigElement
daniel-sol 6e8b0bb
Rename names in Enum
daniel-sol c8b1ed2
Add fields in PydanticModels
daniel-sol cef4f19
Move from dict keys to attributes
daniel-sol ff88e21
Change default value
daniel-sol a3a4e5f
Implement pydantic all way through
daniel-sol 9ddb16b
Update pickled data
daniel-sol 76719c5
Refactor to ensure all rft obs subtypes get written
daniel-sol 6fdd7e2
Add default for subtype and make columns computed_field
daniel-sol 35598de
Correct reading of return values from function
daniel-sol 8ed1a01
Correct datamodel
daniel-sol b4f3704
Simplify functions by passing pydantic object
daniel-sol 68ae8f2
Fix writing of gendata_rft string
daniel-sol 266531f
Change if statement to check against uppercase
daniel-sol 75575d4
Introduce SummaryConfigElement for completeness
daniel-sol 3929dbf
Simplify regex to fix test
daniel-sol f28a5f9
Reuse ConfigElement in reading data
daniel-sol bad32fc
Update docstrings
daniel-sol 91f2f46
Refactor PluginArgument and its use
daniel-sol 7fa41bf
Convert zonemap from key to attribute
daniel-sol 4754e9d
Update tests and test data
daniel-sol cd38710
Fix mypy issues
daniel-sol 8258681
Isort
daniel-sol ce0ffb5
Black
daniel-sol 49ba938
Anonymise test data
daniel-sol e4f5c4a
Add debug statement
daniel-sol 6aaecb4
Add suffix, and set folder read only
daniel-sol 44bc614
Ensure empty string at init
daniel-sol 758682c
Correct writing of time stamped string
daniel-sol f41dce1
Ruff
daniel-sol ee5adcc
Remove upload workflow
daniel-sol 73cc440
Revert hook implementations
daniel-sol 320e5a8
Ruff
daniel-sol 2308db2
More ruff format
daniel-sol File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,325 @@ | ||
"""Pydantic models for genertobs""" | ||
|
||
import logging | ||
import warnings | ||
from enum import Enum | ||
from pathlib import Path | ||
from typing import Dict, List, Union | ||
|
||
from pydantic import ( | ||
BaseModel, | ||
ConfigDict, | ||
Field, | ||
RootModel, | ||
computed_field, | ||
field_validator, | ||
model_validator, | ||
) | ||
|
||
|
||
def is_number(tocheck): | ||
"""Check that variable can be converted to number | ||
|
||
Args: | ||
tocheck (something): what shall be checked | ||
|
||
Returns: | ||
bool: check passed or not | ||
""" | ||
try: | ||
float(tocheck) | ||
return True | ||
except TypeError: | ||
return False | ||
|
||
|
||
def is_percent_range(string): | ||
"""Check if string ending with % is between 0 and 100 | ||
|
||
Args: | ||
string (str): the string to check | ||
|
||
Returns: | ||
bool: True if number is in the range | ||
""" | ||
logger = logging.getLogger(__file__ + ".is_percent_range") | ||
logger.debug("Input is %s", string) | ||
number = float(string.replace("%", "")) | ||
if 0 < number < 100: | ||
return True | ||
|
||
if number > 50: | ||
warnings.warn( | ||
"It seems weird to have an error of more than 50%" | ||
f" ({number}, is this correct?)" | ||
) | ||
return False | ||
|
||
|
||
def is_string_convertible_2_percent(error): | ||
"""Check string | ||
|
||
Args: | ||
error (str): string to check | ||
|
||
Raises: | ||
ValueError: if string does not end with a percent sign | ||
TypeError: if string cannot be converted to a number | ||
ValueError: if the number ends with %, but is not between 0 and 100 | ||
""" | ||
logger = logging.getLogger(__file__ + ".is_string_convertible_2_percent") | ||
logger.debug("Checking this string %s", error) | ||
try: | ||
if not is_number(error[:-1]): | ||
raise TypeError(f"This: {error} is not convertible to a number") | ||
except TypeError: | ||
pass | ||
|
||
if not error.endswith("%"): | ||
raise ValueError( | ||
f"When default_error ({error}) given as string it must end with a % sign" | ||
) | ||
|
||
if not is_percent_range(error): | ||
raise ValueError(f"The number {error} is not in the valid range 0-100") | ||
|
||
|
||
def check_error_limits(error, err_min, err_max): | ||
"""Check error limits | ||
|
||
Args: | ||
error (Union[str,int,float]): the error to check against | ||
err_min (Union[int,float,None]): the lower limit | ||
err_max (Union[int,float,None]): the higher limit | ||
|
||
|
||
Raises: | ||
ValueError: if err_min is not None when error is not in percent | ||
ValueError: if err_max is not None when error is not in percent | ||
ValueError: if err_min >= max | ||
""" | ||
logger = logging.getLogger(__file__ + ".check_error_limits") | ||
logger.debug("Checking with error: %s, and limits %s-%s", error, err_min, err_max) | ||
if isinstance(error, (float, int)): | ||
if err_min is not None: | ||
raise ValueError( | ||
"default_error specified as an absolute number," | ||
f" doesn't make sense to set a lower limit ({err_min})" | ||
) | ||
if err_max is not None: | ||
raise ValueError( | ||
"default_error specified as an absolute number," | ||
f" doesn't make sense to set a higher limit ({err_max})" | ||
) | ||
else: | ||
if err_min is not None and err_max is not None and err_min >= err_max: | ||
raise ValueError( | ||
f"When using limits, the minimum must be lower than the maximum\n" | ||
f"{err_min}-{err_max}" | ||
) | ||
|
||
|
||
class ObservationType(Enum): | ||
"""The valid datatypes in a config file | ||
|
||
Args: | ||
Enum (Enum): Enumerator | ||
""" | ||
|
||
SUMMARY = "summary" | ||
RFT = "rft" | ||
|
||
|
||
class RftType(Enum): | ||
"""Valid Rft types | ||
|
||
Args: | ||
Enum (Enum): Enumerator | ||
""" | ||
|
||
PRESSURE = "pressure" | ||
SWAT = "saturation_water" | ||
SOIL = "saturation_oil" | ||
SGAS = "saturation_gas" | ||
|
||
|
||
class ElementMetaData(BaseModel): | ||
"""Pattern for Metadata element for observations | ||
|
||
Args: | ||
BaseModel (BaseModel): pydantic BaseModel | ||
""" | ||
|
||
subtype: RftType = Field( | ||
default=RftType.PRESSURE, | ||
description=f"Type of rft observation, can be any of {RftType.__members__}", | ||
) | ||
|
||
@property | ||
@computed_field | ||
def columns(self) -> Dict[RftType, Dict[str, str]]: | ||
"""Define columns to expect | ||
|
||
Returns: | ||
Dict[RftType, Dict[str, str]]: the expected column with units | ||
""" | ||
if self.subtype == RftType.PRESSURE: | ||
units = {"unit": "bar"} | ||
else: | ||
units = {"unit": "fraction"} | ||
return {self.subtype: units} | ||
|
||
|
||
class PluginArguments(BaseModel): | ||
"""Plugin arguments for config element""" | ||
|
||
model_config = ConfigDict(extra="forbid") | ||
zonemap: str = Field( | ||
default="", | ||
description="path to file with mapping between zone names and grid layers", | ||
) | ||
trajectories: str = Field( | ||
default="", description="path to folder with trajectory files" | ||
) | ||
|
||
|
||
class ConfigElement(BaseModel): | ||
"""Element in a config file""" | ||
|
||
name: str = Field(min_length=5, description="Name of observation") | ||
type: ObservationType = Field(description="Type of observation") | ||
observation: str = Field( | ||
description="path to file containing observations, this can be any csv" | ||
" like file,\n i.e textfile or spreadsheet", | ||
) | ||
active: bool = Field( | ||
default=True, | ||
description="If the observation element shall be used in\n " | ||
"generation of ert observations", | ||
) | ||
default_error: Union[str, float, int] = Field( | ||
default=None, | ||
description="Optional argument. Error to be used\n. Used only when" | ||
"no error column present or where error column is empty." | ||
" Can be supplied as any number, and even with a percentage sign", | ||
) | ||
|
||
min_error: Union[int, float] = Field( | ||
default=None, | ||
description="Optional argument. Minimum error, only allowed " | ||
"when default_error is in percent", | ||
) | ||
max_error: Union[int, float] = Field( | ||
default=None, | ||
description="Optional argument. Maximum error, only allowed " | ||
"when default_error is in percent", | ||
) | ||
|
||
alias_file: str = Field(default=None, description="Name of file with aliases") | ||
|
||
@field_validator("observation") | ||
@classmethod | ||
def validate_path_exists(cls, observation_path: str): | ||
"""Check that observation file exists | ||
|
||
Args: | ||
observation_path (str): the path to check | ||
|
||
Raises: | ||
OSError: if observation_path does not exist | ||
""" | ||
if not Path(observation_path).exists(): | ||
raise OSError(f"Input observation file {observation_path}, does not exist") | ||
return observation_path | ||
|
||
@field_validator("default_error") | ||
@classmethod | ||
def validate_default_error(cls, error: Union[str, int, float]): | ||
"""Check that if error is string, and if so then check that it is in % | ||
|
||
Args: | ||
observation_path (str): the path to check | ||
|
||
Raises: | ||
OSError: if observation_path does not exist | ||
""" | ||
try: | ||
is_string_convertible_2_percent(error) | ||
except AttributeError: | ||
if error < 0: # type: ignore | ||
raise ValueError(f"default error cannot be negative {error}") # pylint: disable=raise-missing-from | ||
return error | ||
|
||
@model_validator(mode="after") | ||
def check_when_default_is_number(self): | ||
"""Check | ||
|
||
Returns: | ||
Self: self | ||
""" | ||
check_error_limits(self.default_error, self.min_error, self.max_error) | ||
return self | ||
|
||
|
||
class SummaryConfigElement(ConfigElement): | ||
"""ConfigElement for Summary data | ||
|
||
Args: | ||
ConfigElement (ConfigElement): base observation config element | ||
""" | ||
|
||
model_config = ConfigDict(extra="forbid") | ||
|
||
|
||
class RftConfigElement(ConfigElement): | ||
"""Config element for Rft | ||
contains some additional fields | ||
|
||
Args: | ||
ConfigElement (pydantic model): base observation config element | ||
""" | ||
|
||
plugin_arguments: PluginArguments # = Field(default=None) | ||
metadata: ElementMetaData = Field( | ||
default=ElementMetaData(), | ||
description="Metadata describing the type", | ||
) | ||
|
||
# @field_validator("type") | ||
# @classmethod | ||
# def validate_of_rft_type(cls, observation_type: ObservationType): | ||
# """validate that type is rft | ||
|
||
# Args: | ||
# observation_type (ObservationType): the type of observation | ||
|
||
# Raises: | ||
# TypeError: if type is not RFT | ||
|
||
# Returns: | ||
# ObservationType: type of observations | ||
# """ | ||
# if observation_type != ObservationType.RFT: | ||
# raise TypeError(f"This is not rft type, but {observation_type}") | ||
# return observation_type | ||
|
||
|
||
class ObservationsConfig(RootModel): | ||
"""Root model for config file | ||
|
||
Args: | ||
RootModel (Rootmodel): pydantic root model | ||
""" | ||
|
||
root: List[Union[SummaryConfigElement, RftConfigElement]] = Field( | ||
description="What type of observation", | ||
) | ||
|
||
def __iter__(self): | ||
return iter(self.root) | ||
|
||
def __getitem__(self, item): | ||
return self.root[item] | ||
|
||
def __len__(self): | ||
return len(self.root) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem to check if the string contains %
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I see that the checking is done before calling the function. Could probably update the docstring.