Skip to content

Conditional Validation #325

Description

@HorridModz

I have a value that I only want to validate if a different argument is provided. Here is a simplified version of my code:

schema = Schema({	Optional("lib_file"): Use(open),
					Optional("offset"): Use(lambda "offset": int(offset, 16))
				})
schema.validate({"lib_file": "C:\lib.so", offset: "0xFFF"})

Here, offset is dependent on lib_file: It is required if lib_file is provided, but it should not be provided if lib_file is not provided - an "exclusive nor" relationship. Thus, offset should be validated conditionally, rather than being simply required or optional.

I have figured out a quite hacky way to do this, namely:

lib_file = "C:\lib.so"
offset = "0xFFF"
schema = Schema({	Optional("lib_file"): Use(open),
				    "offset": Or(And(lambda offset: offset is not None or lib_file is None,
                                               	error="If you provide a libfile, you must also provide an offset"),
										Use(lambda offset: int(offset, 16)))
				}, ignore_extra_keys=True)
schema.validate({"lib_file": lib_file, "offset": offset})

This yields the desired effects:

# Okay
lib_file = "C:\lib.so"
offset = "0xFFF"
schema.validate({"lib_file": lib_file, "offset": offset})
# Okay
lib_file = None
offset = None
schema.validate({"lib_file": lib_file, "offset": offset})
# Not okay - offset but no lib
lib_file = "C:\lib.so"
offset = None
schema.validate({"lib_file": lib_file, "offset": offset})

schema.SchemaError: If you provide a libfile, you must also provide an offset

However, this way is not at all ideal. First off, I need to access the store lib_file value in a variable and access it inside the schema, since I'm accessing it inside offset's validation logic. Second, this is extremely ugly and bulky. It would be great if there was some way to create relations (mutually exclusive, jointly exhaustive, exclusive nor) between keys, and make a key optional or required depending on if another key exists or not:

Required_if("other_key", "this_key")
Optional_if("other_key", "this_key")
# Perhaps more complex logic could be implemented around a type of design pattern, allowing for more complicated conditions (just like with the validation itself)

Or maybe a dictionary of relationships passed to the Schema constructor:

relationships=[{schema.MUTUALLY_EXCLUSIVE: ["key1", "key2", "key3"]},
			   {schema.JOINTLY_EXHAUSTIVE: ["key7", "key8"]}]

This would make my code as simple as:

# Idea 1
schema = Schema({	Optional("lib_file"): Use(open),
					Required_if("lib_file", "offset"): Use(lambda "offset": int(offset, 16))
				})
# OR:
# Idea 2
schema = Schema({	Optional("lib_file"): Use(open),
					Optional("offset"): Use(lambda "offset": int(offset, 16))
				}, relationships=[{schema.EXCLUSIVE_NOR: ["offset", "lib_file"]}])

Or maybe there's already a way to do what I want that I'm not aware of?

Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions