Skip to content

Reference

sopsy

SOPSy, a Python wrapper around SOPS.

SOPS binary must be installed and available in your $PATH.

Modules:

Name Description
errors

SOPSy Exceptions.

sopsy

SOPSy, a Python wrapper around SOPS.

utils

SOPSy utils.

Classes:

Name Description
Sops

SOPS file object.

SopsyCommandFailedError

Sopsy could not execute SOPS command successfully.

SopsyCommandNotFoundError

Sopsy could not find SOPS command in PATH.

SopsyConfigNotFoundError

Sopsy could not find the given configuration file.

SopsyError

Sopsy base exception class.

SopsyInOutType

SOPS output types.

SopsyInputSource

SOPS input source.

SopsyUnparsableOutpoutTypeError

Sopsy could not read SOPS output content.

Sops

Sops(
    file: str | Path | bytes,
    *,
    binary_path: str | Path | None = None,
    config: str | Path | None = None,
    config_dict: dict[str, Any] | None = None,
    extract: str | None = None,
    in_place: bool = False,
    input_type: str | SopsyInOutType | None = None,
    output: str | Path | None = None,
    output_type: str | SopsyInOutType | None = None,
    input_source: SopsyInputSource = FILE
)

SOPS file object.

Attributes:

Name Type Description
bin Path

Path to the SOPS binary.

file str | Path | bytes

Path to the SOPS file or content to encrypt/decrypt.

global_args list[str]

The list of arguments that will be passed to the sops shell command. It can be used to customize it. Use it only if you know what you are doing.

input_source SopsyInputSource

Wether input data come from a file or stdin.

Examples:

>>> from pathlib import Path
>>> from sopsy import Sops
>>> sops = Sops(
>>>     binary_path="/app/bin/my_custom_sops",
>>>     config=Path(".config/sops.yml"),
>>>     file=Path("secrets.json"),
>>> )

Parameters:

Name Type Description Default

file

str | Path | bytes

Path to the SOPS file or content to encrypt/decrypt.

required

config

str | Path | None

Path to a custom SOPS config file.

None

config_dict

dict[str, Any] | None

Allow to pass SOPS config as a python dict.

None

extract

str | None

Extract a specific key or branch from the input document.

None

in_place

bool

Write output back to the same file instead of stdout.

False

input_type

str | SopsyInOutType | None

If not set, sops will use the file's extension to determine the type.

None

output

str | Path | None

Save the output after encryption or decryption to the file specified.

None

output_type

str | SopsyInOutType | None

If not set, sops will use the input file's extension to determine the output format.

None

binary_path

str | Path | None

Path to the SOPS binary. If not defined it will search for it in the PATH environment variable.

None

input_source

SopsyInputSource

Wether input data come from a file or stdin.

FILE

Methods:

Name Description
decrypt

Decrypt SOPS file.

encrypt

Encrypt SOPS file.

get

Get a specific key from a SOPS encrypted file.

rotate

Rotate encryption keys and re-encrypt values from SOPS file.

Source code in src/sopsy/sopsy.py
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
def __init__(  # noqa: C901
    self,
    file: str | Path | bytes,
    *,
    binary_path: str | Path | None = None,
    config: str | Path | None = None,
    config_dict: dict[str, Any] | None = None,
    extract: str | None = None,
    in_place: bool = False,
    input_type: str | SopsyInOutType | None = None,
    output: str | Path | None = None,
    output_type: str | SopsyInOutType | None = None,
    input_source: SopsyInputSource = SopsyInputSource.FILE,
) -> None:
    """Initialize SOPS object.

    Examples:
        >>> from pathlib import Path
        >>> from sopsy import Sops
        >>> sops = Sops(
        >>>     binary_path="/app/bin/my_custom_sops",
        >>>     config=Path(".config/sops.yml"),
        >>>     file=Path("secrets.json"),
        >>> )

    Args:
        file: Path to the SOPS file or content to encrypt/decrypt.
        config: Path to a custom SOPS config file.
        config_dict: Allow to pass SOPS config as a python dict.
        extract: Extract a specific key or branch from the input document.
        in_place: Write output back to the same file instead of stdout.
        input_type: If not set, sops will use the file's extension to determine
            the type.
        output: Save the output after encryption or decryption to the file
            specified.
        output_type: If not set, sops will use the input file's extension to
            determine the output format.
        binary_path: Path to the SOPS binary. If not defined it will search for it
            in the PATH environment variable.
        input_source: Wether input data come from a file or stdin.
    """
    self.bin: Path = Path(binary_path) if binary_path else Path("sops")
    self.file: str | Path | bytes = file
    self.global_args: list[str] = []
    self.input_source: SopsyInputSource = input_source
    if extract:
        self.global_args.extend(["--extract", extract])
    if in_place:
        self.global_args.extend(["--in-place"])
    if input_type:
        self.global_args.extend(["--input-type", str(input_type)])
        self.input_type: str | SopsyInOutType | None = input_type
    if output:
        self.global_args.extend(["--output", str(output)])
    if output_type:
        self.global_args.extend(["--output-type", str(output_type)])

    if isinstance(config, str):
        config = Path(config)
    if config_dict is None:
        config_dict = {}
    config_dict = build_config(config_path=config, config_dict=config_dict)
    with tempfile.NamedTemporaryFile(mode="w", delete=False) as fp:
        yaml.dump(config_dict, fp)
        config_tmp = fp.name
    self.config: list[str] = ["--config", config_tmp]

    if input_source == SopsyInputSource.STDIN and not input_type:
        msg = "When using stdin source, input MUST be specified"
        raise SopsyError(msg)

    if input_source == SopsyInputSource.STDIN and isinstance(file, Path):
        msg = "Path type cannot be used with stdin input source."
        raise SopsyError(msg)

    if not shutil.which(self.bin):
        msg = (
            f"{self.bin} command not found, "
            "you may need to install it and/or add it to your PATH"
        )
        raise SopsyCommandNotFoundError(msg)

decrypt

decrypt(*, to_dict: bool = True) -> str | bytes | dict[str, Any] | None

Decrypt SOPS file.

Examples:

>>> from sopsy import Sops, SopsyInOutType
>>> sops = Sops("secrets.json", output_type=SopsyInOutType.YAML)
>>> sops.decrypt(to_dict=False)
hello: world

Parameters:

Name Type Description Default
to_dict
bool

Return the output as a Python dict.

True

Returns:

Type Description
str | bytes | dict[str, Any] | None

The output of the sops command.

Source code in src/sopsy/sopsy.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
def decrypt(self, *, to_dict: bool = True) -> str | bytes | dict[str, Any] | None:
    """Decrypt SOPS file.

    Examples:
        >>> from sopsy import Sops, SopsyInOutType
        >>> sops = Sops("secrets.json", output_type=SopsyInOutType.YAML)
        >>> sops.decrypt(to_dict=False)
        hello: world

    Args:
        to_dict: Return the output as a Python dict.

    Returns:
        The output of the sops command.
    """
    cmd = [str(self.bin), *self.config, "decrypt", *self.global_args]
    if self.input_source == SopsyInputSource.STDIN:
        cmd.extend(["--filename-override", f"dummy.{self.input_type}"])
        input_data = self.file
        assert not isinstance(input_data, Path)  # noqa: S101
    else:
        cmd.append(str(self.file))
        input_data = None
    return run_cmd(cmd, to_dict=to_dict, input_data=input_data)

encrypt

encrypt(*, to_dict: bool = True) -> str | bytes | dict[str, Any] | None

Encrypt SOPS file.

Examples:

>>> import json
>>> from pathlib import Path
>>> from sopsy import Sops
>>> secrets = Path("secrets.json")
>>> secrets.write_text(json.dumps({"hello": "world"}))
>>> sops = Sops(secrets, in_place=True)
>>> sops.encrypt()

Parameters:

Name Type Description Default
to_dict
bool

Return the output as a Python dict.

True

Returns:

Type Description
str | bytes | dict[str, Any] | None

The output of the sops command.

Source code in src/sopsy/sopsy.py
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
def encrypt(self, *, to_dict: bool = True) -> str | bytes | dict[str, Any] | None:
    """Encrypt SOPS file.

    Examples:
        >>> import json
        >>> from pathlib import Path
        >>> from sopsy import Sops
        >>> secrets = Path("secrets.json")
        >>> secrets.write_text(json.dumps({"hello": "world"}))
        >>> sops = Sops(secrets, in_place=True)
        >>> sops.encrypt()

    Args:
        to_dict: Return the output as a Python dict.

    Returns:
        The output of the sops command.
    """
    cmd = [str(self.bin), *self.config, "encrypt", *self.global_args]
    if self.input_source == SopsyInputSource.STDIN:
        cmd.extend(["--filename-override", f"dummy.{self.input_type}"])
        input_data = self.file
        assert not isinstance(input_data, Path)  # noqa: S101
    else:
        cmd.append(str(self.file))
        input_data = None
    return run_cmd(cmd, to_dict=to_dict, input_data=input_data)

get

get(key: str, *, default: Any = None) -> Any

Get a specific key from a SOPS encrypted file.

Examples:

>>> from sopsy import Sops
>>> sops = Sops("secrets.json")
>>> sops.get("hello")
b'world'
>>> sops.get("nonexistent", default="DefaultValue")
'DefaultValue'

Parameters:

Name Type Description Default
key
str

The key to fetch in the SOPS file content.

required
default
Any

A default value in case the key does not exist or is empty.

None

Returns:

Type Description
Any

The value of the given key, or the default value.

Source code in src/sopsy/sopsy.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def get(self, key: str, *, default: Any = None) -> Any:  # noqa: ANN401
    """Get a specific key from a SOPS encrypted file.

    Examples:
        >>> from sopsy import Sops
        >>> sops = Sops("secrets.json")
        >>> sops.get("hello")
        b'world'
        >>> sops.get("nonexistent", default="DefaultValue")
        'DefaultValue'

    Args:
        key: The key to fetch in the SOPS file content.
        default: A default value in case the key does not exist or is empty.

    Returns:
        The value of the given key, or the default value.
    """
    data: dict[Any, Any] = self.decrypt()  # pyright: ignore[reportAssignmentType]
    return data.get(key) or default

rotate

rotate(*, to_dict: bool = True) -> str | bytes | dict[str, Any] | None

Rotate encryption keys and re-encrypt values from SOPS file.

Examples:

>>> from sopsy import Sops
>>> sops = Sops("secrets.json", in_place=True)
>>> sops.rotate()

Parameters:

Name Type Description Default
to_dict
bool

Return the output as a Python dict.

True

Returns:

Type Description
str | bytes | dict[str, Any] | None

The output of the sops command.

Source code in src/sopsy/sopsy.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
def rotate(self, *, to_dict: bool = True) -> str | bytes | dict[str, Any] | None:
    """Rotate encryption keys and re-encrypt values from SOPS file.

    Examples:
        >>> from sopsy import Sops
        >>> sops = Sops("secrets.json", in_place=True)
        >>> sops.rotate()

    Args:
        to_dict: Return the output as a Python dict.

    Returns:
        The output of the sops command.
    """
    cmd = [str(self.bin), *self.config, "rotate", *self.global_args, str(self.file)]
    return run_cmd(cmd, to_dict=to_dict)

SopsyCommandFailedError

Bases: SopsyError

Sopsy could not execute SOPS command successfully.

SopsyCommandNotFoundError

Bases: SopsyError

Sopsy could not find SOPS command in PATH.

SopsyConfigNotFoundError

Bases: SopsyError

Sopsy could not find the given configuration file.

SopsyError

Bases: Exception

Sopsy base exception class.

SopsyInOutType

Bases: Enum

SOPS output types.

Intended to be passed on Sops().input_type and Sops().output_type.

Attributes:

Name Type Description
BINARY str

Binary type.

DOTENV str

DotEnv type.

JSON str

JSON type.

YAML str

YAML type.

SopsyInputSource

Bases: Enum

SOPS input source.

Used to determinie wether input data come from a file or stdin.

Attributes:

Name Type Description
FILE str

From an on-disk file.

STDIN str

From stdin.

SopsyUnparsableOutpoutTypeError

Bases: SopsyError

Sopsy could not read SOPS output content.