pylego 0.1.31.1__py3-none-any.whl → 0.1.33__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pylego/lego.go CHANGED
@@ -11,8 +11,12 @@ import (
11
11
  "encoding/pem"
12
12
  "errors"
13
13
  "fmt"
14
+ "net"
14
15
  "os"
16
+ "strings"
17
+ "time"
15
18
 
19
+ "github.com/go-acme/lego/v4/acme"
16
20
  "github.com/go-acme/lego/v4/certcrypto"
17
21
  "github.com/go-acme/lego/v4/certificate"
18
22
  "github.com/go-acme/lego/v4/challenge/dns01"
@@ -23,13 +27,30 @@ import (
23
27
  "github.com/go-acme/lego/v4/registration"
24
28
  )
25
29
 
30
+ // Error code constants
31
+ const (
32
+ ErrInvalidArguments = "invalid_arguments"
33
+ ErrInvalidEnvironment = "invalid_environment"
34
+ ErrCertificateRequestFailed = "certificate_request_failed"
35
+ ErrInvalidPrivateKey = "invalid_private_key"
36
+ ErrKeyGenerationFailed = "key_generation_failed"
37
+ ErrLegoClientCreationFailed = "lego_client_creation_failed"
38
+ ErrDNSProviderFailed = "dns_provider_failed"
39
+ ErrAccountRegistrationFailed = "account_registration_failed"
40
+ ErrInvalidCSR = "invalid_csr"
41
+ ErrCertificateObtainFailed = "certificate_obtain_failed"
42
+ ErrNetworkError = "network_error"
43
+ ErrMarshalingFailed = "marshaling_failed"
44
+ )
45
+
26
46
  type LegoInputArgs struct {
27
- Email string `json:"email"`
28
- PrivateKey string `json:"private_key,omitempty"`
29
- Server string `json:"server"`
30
- CSR string `json:"csr"`
31
- Plugin string `json:"plugin"`
32
- Env map[string]string
47
+ Email string `json:"email"`
48
+ PrivateKey string `json:"private_key,omitempty"`
49
+ Server string `json:"server"`
50
+ CSR string `json:"csr"`
51
+ Plugin string `json:"plugin"`
52
+ Env map[string]string
53
+ DNSPropagationWait int `json:"dns_propagation_wait,omitempty"`
33
54
  }
34
55
 
35
56
  type LegoOutputResponse struct {
@@ -46,42 +67,217 @@ type Metadata struct {
46
67
  Domain string `json:"domain"`
47
68
  }
48
69
 
70
+ type Subproblem struct {
71
+ Type string `json:"type"` // Error type URN
72
+ Detail string `json:"detail"` // Human-readable message
73
+ Identifier Identifier `json:"identifier,omitempty"` // The identifier that caused this subproblem
74
+ }
75
+
76
+ type Identifier struct {
77
+ Type string `json:"type"` // "dns" or "ip"
78
+ Value string `json:"value"` // Domain name or IP address
79
+ }
80
+
81
+ type ErrorResponse struct {
82
+ Type string `json:"type"` // "acme" for CA server errors, "lego" for everything else
83
+ Code string `json:"code"` // Error code or category
84
+ Status *int `json:"status,omitempty"` // HTTP status if applicable (ACME errors)
85
+ Detail string `json:"detail"` // Human-readable message
86
+ ACMEType string `json:"acme_type,omitempty"` // Full ACME URN if applicable
87
+ Subproblems []Subproblem `json:"subproblems,omitempty"` // Detailed subproblems from ACME errors
88
+ }
89
+
90
+ type LegoResponse struct {
91
+ Success bool `json:"success"`
92
+ Error *ErrorResponse `json:"error,omitempty"`
93
+ Data *LegoOutputResponse `json:"data,omitempty"`
94
+ }
95
+
96
+ func isNetworkError(err error) bool {
97
+ if err == nil {
98
+ return false
99
+ }
100
+ var (
101
+ netErr net.Error
102
+ dnsErr *net.DNSError
103
+ opErr *net.OpError
104
+ )
105
+
106
+ switch {
107
+ case errors.As(err, &netErr):
108
+ return true
109
+ case errors.As(err, &dnsErr):
110
+ return true
111
+ case errors.As(err, &opErr):
112
+ return true
113
+ default:
114
+ return false
115
+ }
116
+ }
117
+
118
+ func extractSubproblems(problemDetails *acme.ProblemDetails) []Subproblem {
119
+ var subproblems []Subproblem
120
+ for _, sub := range problemDetails.SubProblems {
121
+ subCode := "unknown"
122
+ if sub.Type != "" {
123
+ parts := strings.Split(sub.Type, ":")
124
+ if len(parts) > 0 {
125
+ subCode = parts[len(parts)-1]
126
+ }
127
+ }
128
+ subproblems = append(subproblems, Subproblem{
129
+ Type: subCode,
130
+ Detail: sub.Detail,
131
+ Identifier: Identifier{
132
+ Type: sub.Identifier.Type,
133
+ Value: sub.Identifier.Value,
134
+ },
135
+ })
136
+ }
137
+ return subproblems
138
+ }
139
+
140
+ func wrapError(err error, context string) *ErrorResponse {
141
+ if err == nil {
142
+ return nil
143
+ }
144
+
145
+ var ctxErr *contextError
146
+ if errors.As(err, &ctxErr) {
147
+ context = ctxErr.context
148
+ err = ctxErr.original
149
+ }
150
+
151
+ var problemDetails *acme.ProblemDetails
152
+ if errors.As(err, &problemDetails) {
153
+ code := "unknown"
154
+ if problemDetails.Type != "" {
155
+ parts := strings.Split(problemDetails.Type, ":")
156
+ if len(parts) > 0 {
157
+ code = parts[len(parts)-1]
158
+ }
159
+ }
160
+ status := problemDetails.HTTPStatus
161
+
162
+ return &ErrorResponse{
163
+ Type: "acme",
164
+ Code: code,
165
+ Status: &status,
166
+ Detail: problemDetails.Detail,
167
+ ACMEType: problemDetails.Type,
168
+ Subproblems: extractSubproblems(problemDetails),
169
+ }
170
+ }
171
+
172
+ if isNetworkError(err) {
173
+ return &ErrorResponse{
174
+ Type: "lego",
175
+ Code: ErrNetworkError,
176
+ Detail: err.Error(),
177
+ }
178
+ }
179
+
180
+ return &ErrorResponse{
181
+ Type: "lego",
182
+ Code: context,
183
+ Detail: err.Error(),
184
+ }
185
+ }
186
+
187
+ func buildErrorResponse(err error, context string) *C.char {
188
+ response := LegoResponse{
189
+ Success: false,
190
+ Error: wrapError(err, context),
191
+ }
192
+ responseJSON, marshalErr := json.Marshal(response)
193
+ if marshalErr == nil {
194
+ return C.CString(string(responseJSON))
195
+ }
196
+
197
+ fallbackResponse := LegoResponse{
198
+ Success: false,
199
+ Error: &ErrorResponse{
200
+ Type: "lego",
201
+ Code: ErrMarshalingFailed,
202
+ Detail: marshalErr.Error(),
203
+ },
204
+ }
205
+ fallbackJSON, _ := json.Marshal(fallbackResponse)
206
+
207
+ return C.CString(string(fallbackJSON))
208
+ }
209
+
210
+ func buildSuccessResponse(data *LegoOutputResponse) *C.char {
211
+ response := LegoResponse{
212
+ Success: true,
213
+ Data: data,
214
+ }
215
+ responseJSON, marshalErr := json.Marshal(response)
216
+ if marshalErr == nil {
217
+ return C.CString(string(responseJSON))
218
+ }
219
+
220
+ fallbackResponse := LegoResponse{
221
+ Success: false,
222
+ Error: &ErrorResponse{
223
+ Type: "lego",
224
+ Code: ErrMarshalingFailed,
225
+ Detail: marshalErr.Error(),
226
+ },
227
+ }
228
+ fallbackJSON, _ := json.Marshal(fallbackResponse)
229
+
230
+ return C.CString(string(fallbackJSON))
231
+ }
232
+
233
+ type contextError struct {
234
+ original error
235
+ context string
236
+ }
237
+
238
+ func (e *contextError) Error() string {
239
+ return e.original.Error()
240
+ }
241
+
242
+ func (e *contextError) Unwrap() error {
243
+ return e.original
244
+ }
245
+
246
+ func wrapWithContext(err error, context string) error {
247
+ return &contextError{original: err, context: context}
248
+ }
249
+
49
250
  //export RunLegoCommand
50
251
  func RunLegoCommand(message *C.char) *C.char {
51
252
  CLIArgs, err := extractArguments(C.GoString(message))
52
253
  if err != nil {
53
- return C.CString(fmt.Sprint("error: couldn't extract arguments: ", err))
254
+ return buildErrorResponse(err, ErrInvalidArguments)
54
255
  }
55
256
  for k, v := range CLIArgs.Env {
56
257
  if err := os.Setenv(k, v); err != nil {
57
- return C.CString(fmt.Sprint("error: couldn't load environment variables: ", err))
258
+ return buildErrorResponse(err, ErrInvalidEnvironment)
58
259
  }
59
260
 
60
261
  }
61
- certificate, err := requestCertificate(CLIArgs.Email, CLIArgs.PrivateKey, CLIArgs.Server, CLIArgs.CSR, CLIArgs.Plugin)
262
+ certificate, err := requestCertificate(CLIArgs.Email, CLIArgs.PrivateKey, CLIArgs.Server, CLIArgs.CSR, CLIArgs.Plugin, CLIArgs.DNSPropagationWait)
62
263
  if err != nil {
63
- return C.CString(fmt.Sprint("error: couldn't request certificate: ", err))
264
+ return buildErrorResponse(err, ErrCertificateRequestFailed)
64
265
  }
65
- response_json, err := json.Marshal(certificate)
66
- if err != nil {
67
- return C.CString(fmt.Sprint("error: coudn't build response message: ", err))
68
- }
69
- return_message_ptr := C.CString(string(response_json))
70
- return return_message_ptr
266
+ return buildSuccessResponse(certificate)
71
267
  }
72
268
 
73
- func requestCertificate(email, privateKeyPem, server, csr, plugin string) (*LegoOutputResponse, error) {
269
+ func requestCertificate(email, privateKeyPem, server, csr, plugin string, propagationWait int) (*LegoOutputResponse, error) {
74
270
  var privateKey crypto.PrivateKey
75
271
  if privateKeyPem != "" {
76
272
  parsedKey, err := certcrypto.ParsePEMPrivateKey([]byte(privateKeyPem))
77
273
  if err != nil {
78
- return nil, fmt.Errorf("couldn't parse private key: %s", err)
274
+ return nil, wrapWithContext(err, ErrInvalidPrivateKey)
79
275
  }
80
276
  privateKey = parsedKey
81
277
  } else {
82
278
  generatedKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
83
279
  if err != nil {
84
- return nil, fmt.Errorf("couldn't generate priv key: %s", err)
280
+ return nil, wrapWithContext(err, ErrKeyGenerationFailed)
85
281
  }
86
282
  privateKey = generatedKey
87
283
  }
@@ -96,27 +292,27 @@ func requestCertificate(email, privateKeyPem, server, csr, plugin string) (*Lego
96
292
 
97
293
  client, err := lego.NewClient(config)
98
294
  if err != nil {
99
- return nil, fmt.Errorf("couldn't create lego client: %s", err)
295
+ return nil, wrapWithContext(err, ErrLegoClientCreationFailed)
100
296
  }
101
297
 
102
- err = configureClientChallenges(client, plugin)
298
+ err = configureClientChallenges(client, plugin, propagationWait)
103
299
  if err != nil {
104
- return nil, fmt.Errorf("couldn't configure client challenges: %s", err)
300
+ return nil, wrapWithContext(err, ErrDNSProviderFailed)
105
301
  }
106
302
 
107
303
  reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
108
304
  if err != nil {
109
- return nil, fmt.Errorf("couldn't register user: %s", err)
305
+ return nil, wrapWithContext(err, ErrAccountRegistrationFailed)
110
306
  }
111
307
  user.Registration = reg
112
308
 
113
309
  block, _ := pem.Decode([]byte(csr))
114
310
  if block == nil || block.Type != "CERTIFICATE REQUEST" {
115
- return nil, errors.New("failed to decode PEM block containing certificate request")
311
+ return nil, wrapWithContext(errors.New("failed to decode PEM block"), ErrInvalidCSR)
116
312
  }
117
313
  csrObject, err := x509.ParseCertificateRequest(block.Bytes)
118
314
  if err != nil {
119
- return nil, fmt.Errorf("failed to parse certificate request: %s", err)
315
+ return nil, wrapWithContext(err, ErrInvalidCSR)
120
316
  }
121
317
  request := certificate.ObtainForCSRRequest{
122
318
  CSR: csrObject,
@@ -124,7 +320,7 @@ func requestCertificate(email, privateKeyPem, server, csr, plugin string) (*Lego
124
320
  }
125
321
  certificates, err := client.Certificate.ObtainForCSR(request)
126
322
  if err != nil {
127
- return nil, fmt.Errorf("coudn't obtain cert: %s", err)
323
+ return nil, wrapWithContext(err, ErrCertificateObtainFailed)
128
324
  }
129
325
 
130
326
  return &LegoOutputResponse{
@@ -140,7 +336,7 @@ func requestCertificate(email, privateKeyPem, server, csr, plugin string) (*Lego
140
336
  }, nil
141
337
  }
142
338
 
143
- func configureClientChallenges(client *lego.Client, plugin string) error {
339
+ func configureClientChallenges(client *lego.Client, plugin string, propagationWait int) error {
144
340
  switch plugin {
145
341
  case "", "http":
146
342
  if err := client.Challenge.SetHTTP01Provider(http01.NewProviderServer(os.Getenv("HTTP01_IFACE"), os.Getenv("HTTP01_PORT"))); err != nil {
@@ -157,9 +353,16 @@ func configureClientChallenges(client *lego.Client, plugin string) error {
157
353
  if err != nil {
158
354
  return errors.Join(fmt.Errorf("couldn't create %s provider: ", plugin), err)
159
355
  }
356
+ var wait time.Duration
357
+ if propagationWait > 0 {
358
+ wait = time.Duration(propagationWait) * time.Second
359
+ }
360
+
160
361
  err = client.Challenge.SetDNS01Provider(dnsProvider,
161
362
  dns01.CondOption(os.Getenv("DNS_PROPAGATION_DISABLE_ANS") != "",
162
363
  dns01.DisableAuthoritativeNssPropagationRequirement()),
364
+ dns01.CondOption(wait > 0,
365
+ dns01.PropagationWait(wait, true)),
163
366
  dns01.CondOption(os.Getenv("DNS_PROPAGATION_RNS") != "", dns01.RecursiveNSsPropagationRequirement()))
164
367
  if err != nil {
165
368
  return errors.Join(fmt.Errorf("couldn't set %s DNS provider server: ", plugin), err)
pylego/lego.so CHANGED
Binary file
pylego/pylego.py CHANGED
@@ -10,6 +10,23 @@ so_file = here / ("lego.so")
10
10
  library = ctypes.cdll.LoadLibrary(so_file)
11
11
 
12
12
 
13
+ @dataclass
14
+ class Identifier:
15
+ """ACME identifier (domain or IP)."""
16
+
17
+ type: str # "dns" or "ip"
18
+ value: str # Domain name or IP address
19
+
20
+
21
+ @dataclass
22
+ class Subproblem:
23
+ """ACME subproblem details."""
24
+
25
+ type: str # Error type (e.g., "unauthorized", "dns")
26
+ detail: str # Human-readable message
27
+ identifier: Identifier # The identifier that caused this subproblem
28
+
29
+
13
30
  @dataclass
14
31
  class Metadata:
15
32
  """Extra information returned by the ACME server."""
@@ -31,11 +48,49 @@ class LEGOResponse:
31
48
 
32
49
 
33
50
  class LEGOError(Exception):
34
- """Exceptions that are returned from the LEGO Go library."""
51
+ """Unified exception for all errors returned by the lego invocation.
52
+
53
+ Attributes:
54
+ type: source of the error. "acme" when coming from the ACME server, otherwise "lego".
55
+ code: error code/category. For ACME, this is derived from the ACME problem type; otherwise, it's set by lego.
56
+ status: HTTP status code for ACME errors, None otherwise.
57
+ detail: human-readable description of the error.
58
+ acme_type: full ACME problem type (URN), present only for ACME errors.
59
+ subproblems: list of Subproblem objects with detailed error information.
60
+ info: dictionary with the raw error information returned by the underlying call.
61
+ """
62
+
63
+ def __init__(
64
+ self,
65
+ detail: str,
66
+ *,
67
+ type: str = "lego",
68
+ code: str = "",
69
+ status: int | None = None,
70
+ acme_type: str = "",
71
+ subproblems: list[Subproblem] | None = None,
72
+ info: dict | None = None,
73
+ ):
74
+ # Include code in exception message for better error display
75
+ message = f"[{code}] {detail}" if code else detail
76
+ super().__init__(message)
77
+ self.type = type
78
+ self.code = code
79
+ self.status = status
80
+ self.detail = detail
81
+ self.acme_type = acme_type
82
+ self.subproblems = subproblems or []
83
+ self.info = info or {}
35
84
 
36
85
 
37
86
  def run_lego_command(
38
- email: str, server: str, csr: bytes, env: dict[str, str], plugin: str = "", private_key: str = ""
87
+ email: str,
88
+ server: str,
89
+ csr: bytes,
90
+ env: dict[str, str],
91
+ plugin: str = "",
92
+ private_key: str = "",
93
+ dns_propagation_wait: int | None = None,
39
94
  ) -> LEGOResponse:
40
95
  """Run an arbitrary command in the Lego application. Read more at https://go-acme.github.io.
41
96
 
@@ -47,25 +102,66 @@ def run_lego_command(
47
102
  env: the environment variables required for the chosen plugin.
48
103
  private_key: the private key to be used for the registration on the ACME server (not the private key used to sign the CSR).
49
104
  If not provided, a new one will be generated.
105
+ dns_propagation_wait: optional wait duration for DNS propagation, in seconds (int).
50
106
  """
51
107
  library.RunLegoCommand.restype = ctypes.c_char_p
52
108
  library.RunLegoCommand.argtypes = [ctypes.c_char_p]
53
109
 
110
+ if dns_propagation_wait is not None and dns_propagation_wait < 0:
111
+ raise ValueError("dns_propagation_wait cannot be negative")
112
+
113
+ payload = {
114
+ "email": email,
115
+ "server": server,
116
+ "csr": csr.decode(),
117
+ "plugin": plugin,
118
+ "env": env,
119
+ "private_key": private_key,
120
+ }
121
+ if dns_propagation_wait is not None:
122
+ payload["dns_propagation_wait"] = dns_propagation_wait
123
+
54
124
  message = bytes(
55
- json.dumps(
56
- {
57
- "email": email,
58
- "server": server,
59
- "csr": csr.decode(),
60
- "plugin": plugin,
61
- "env": env,
62
- "private_key": private_key,
63
- }
64
- ),
125
+ json.dumps(payload),
65
126
  "utf-8",
66
127
  )
67
128
  result: bytes = library.RunLegoCommand(message)
68
- if result.startswith(b"error:"):
69
- raise LEGOError(result.decode())
70
- result_dict = json.loads(result.decode("utf-8"))
71
- return LEGOResponse(**{**result_dict, "metadata": Metadata(**result_dict.get("metadata"))})
129
+ result_str = result.decode("utf-8")
130
+
131
+ try:
132
+ result_dict = json.loads(result_str)
133
+ except json.JSONDecodeError as e:
134
+ raise LEGOError(f"Failed to parse response: {result_str}") from e
135
+
136
+ if not result_dict.get("success", False):
137
+ error_info: dict = result_dict.get("error", {})
138
+ err_source = error_info.get("type", "lego")
139
+ detail = error_info.get("detail", "Unknown error occurred")
140
+
141
+ subproblems = []
142
+ for sub_dict in error_info.get("subproblems", []):
143
+ identifier_dict = sub_dict.get("identifier", {})
144
+ subproblems.append(
145
+ Subproblem(
146
+ type=sub_dict.get("type", ""),
147
+ detail=sub_dict.get("detail", ""),
148
+ identifier=Identifier(
149
+ type=identifier_dict.get("type", ""),
150
+ value=identifier_dict.get("value", ""),
151
+ ),
152
+ )
153
+ )
154
+
155
+ info = dict(error_info)
156
+ raise LEGOError(
157
+ detail,
158
+ type="acme" if err_source == "acme" else "lego",
159
+ code=error_info.get("code", ""),
160
+ status=error_info.get("status"),
161
+ acme_type=error_info.get("acme_type", "") if err_source == "acme" else "",
162
+ subproblems=subproblems,
163
+ info=info,
164
+ )
165
+
166
+ data = result_dict.get("data", {})
167
+ return LEGOResponse(**{**data, "metadata": Metadata(**data.get("metadata", {}))})
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pylego
3
- Version: 0.1.31.1
3
+ Version: 0.1.33
4
4
  Summary: A python wrapper package for the lego application written in Golang
5
5
  Author-email: Canonical <telco-engineers@lists.canonical.com>
6
6
  Project-URL: Homepage, https://github.com/canonical/pylego
@@ -63,6 +63,29 @@ On top of the environment variables that LEGO supports, we have some extra ones
63
63
  | `TLSALPN01_IFACE` | Interface for the TLS-ALPN-01 challenge (when `plugin=tls`). Any interface by default. |
64
64
  | `TLSALPN01_PORT` | Port for the TLS-ALPN-01 challenge (when `plugin=tls`). 443 by default. |
65
65
 
66
+ ## Error Handling
67
+
68
+ All errors raised by `run_lego_command()` are `LEGOError` exceptions with structured information:
69
+
70
+ ```python
71
+ from pylego import run_lego_command, LEGOError
72
+
73
+ try:
74
+ result = run_lego_command(...)
75
+ except LEGOError as e:
76
+ print(f"Error: {e}") # Includes error code in message
77
+ print(f"Type: {e.type}") # "acme" (server) or "lego" (client)
78
+ print(f"Code: {e.code}") # e.g., "invalid_csr", "dns_provider_failed"
79
+ print(f"Detail: {e.detail}") # Human-readable message
80
+
81
+ # ACME-specific fields
82
+ if e.type == "acme":
83
+ print(f"Status: {e.status}") # HTTP status code
84
+ print(f"Subproblems: {e.subproblems}") # Validation details per domain
85
+ ```
86
+
87
+ Common error codes: `invalid_csr`, `invalid_private_key`, `dns_provider_failed`, `network_error`, `certificate_obtain_failed`. ACME errors include codes like `unauthorized`, `rateLimited`, `dns`.
88
+
66
89
  ## How does it work?
67
90
 
68
91
  Golang supports building a shared c library from its CLI build tool. We import and use the LEGO application from GoLang, and provide a stub with C bindings so that the shared C binary we produce exposes a C API for other programs to import and utilize. pylego then uses the [ctypes](https://docs.python.org/3/library/ctypes.html) standard library in python to load this binary, and make calls to its methods.
@@ -0,0 +1,11 @@
1
+ pylego/__init__.py,sha256=7rcUcQcOWsOLxTOEXF2ASkwm_7eED1UIXzxdlgKPr5c,82
2
+ pylego/go.mod,sha256=jNERhSIs38s6uxlVwLn8RRpyvax-jgmQWyBfTLY77VI,11182
3
+ pylego/go.sum,sha256=dHaqyMv51JXWRjlWu_fx_pftQul6kgUo8hJButcuJUI,143991
4
+ pylego/lego.go,sha256=zauvVfyFVfH_4Z1lAiTAYRt-tU-0Z4_xmVjKhkP7uu8,11180
5
+ pylego/lego.so,sha256=wAWQFSFsD2vd1nGr3FTdGwUUfliICwvxGhMnvSf7m9c,87373536
6
+ pylego/pylego.py,sha256=556axo5EwOUGG5ZtsHgfFog-CTkJtOsavdTGLgEAu6k,5522
7
+ pylego-0.1.33.dist-info/licenses/LICENSE,sha256=aklz9Y8CIpFsN61U4jHlJYp4W_8HoDpY-tINlDcdSZY,10934
8
+ pylego-0.1.33.dist-info/METADATA,sha256=NpHliVBR4KXDmSkiL354ciOurbesYKk6hixgGKM7CZQ,6703
9
+ pylego-0.1.33.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
10
+ pylego-0.1.33.dist-info/top_level.txt,sha256=pSOYv55_w90qy3xOvqz_ysSz-X-XRTb-jMpiOyLNnNs,7
11
+ pylego-0.1.33.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,11 +0,0 @@
1
- pylego/__init__.py,sha256=7rcUcQcOWsOLxTOEXF2ASkwm_7eED1UIXzxdlgKPr5c,82
2
- pylego/go.mod,sha256=2zddNtfcY9OT_qrCABQwRB06dvL0QF9kMA1E6lmvOlM,12467
3
- pylego/go.sum,sha256=-Gl6lIAbKb0cjJHr4dVvA4_XGBSHONqL0lKRGHUoYn0,187420
4
- pylego/lego.go,sha256=O_tXZN6DRXlVXJfZdjIXTrDWXyn1UuBeZa_ZzPCnGOA,6004
5
- pylego/lego.so,sha256=nlASF8T39I3b6NodVaPofEqOs1c5Fu6b0zxvn8KI6MM,87291672
6
- pylego/pylego.py,sha256=x9NTkBi1P4ITPhGBUgMCHVBHqFN7NXAJAc9aknKLcgk,2263
7
- pylego-0.1.31.1.dist-info/licenses/LICENSE,sha256=aklz9Y8CIpFsN61U4jHlJYp4W_8HoDpY-tINlDcdSZY,10934
8
- pylego-0.1.31.1.dist-info/METADATA,sha256=rh0voQ1R8_NHfbYvivFzVlMoF5sNOEi6Ua0ikdA6Ea8,5778
9
- pylego-0.1.31.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
- pylego-0.1.31.1.dist-info/top_level.txt,sha256=pSOYv55_w90qy3xOvqz_ysSz-X-XRTb-jMpiOyLNnNs,7
11
- pylego-0.1.31.1.dist-info/RECORD,,