magic-admin 2.1.2__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.
- magic_admin/__init__.py +8 -0
- magic_admin/config.py +9 -0
- magic_admin/error.py +100 -0
- magic_admin/http_client.py +140 -0
- magic_admin/magic.py +53 -0
- magic_admin/response.py +5 -0
- magic_admin/version.py +1 -0
- magic_admin-2.1.2.dist-info/METADATA +172 -0
- magic_admin-2.1.2.dist-info/RECORD +11 -0
- magic_admin-2.1.2.dist-info/WHEEL +5 -0
- magic_admin-2.1.2.dist-info/top_level.txt +1 -0
magic_admin/__init__.py
ADDED
magic_admin/config.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
base_url = "https://api.toaster.magic.link"
|
|
2
|
+
|
|
3
|
+
api_secret_api_key_missing_message = (
|
|
4
|
+
"API secret key is missing. Please specify "
|
|
5
|
+
"an API secret key when you instantiate the `Magic(api_secret_key=<KEY>)` "
|
|
6
|
+
"object or use the environment variable, `MAGIC_API_SECRET_KEY`. You can "
|
|
7
|
+
"get your API secret key from https://dashboard.magic.link. If you are having "
|
|
8
|
+
"trouble, please don't hesitate to reach out to us at support@magic.link"
|
|
9
|
+
)
|
magic_admin/error.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
class MagicError(Exception):
|
|
2
|
+
def __init__(self, message=None):
|
|
3
|
+
super().__init__(message)
|
|
4
|
+
self._message = message
|
|
5
|
+
|
|
6
|
+
def __str__(self):
|
|
7
|
+
return self._message or "<empty message>"
|
|
8
|
+
|
|
9
|
+
def __repr__(self):
|
|
10
|
+
return "{error_class}(message={message!r})".format(
|
|
11
|
+
error_class=self.__class__.__name__,
|
|
12
|
+
message=self._message,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def to_dict(self):
|
|
16
|
+
return {"message": str(self)}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DIDTokenInvalid(MagicError):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class DIDTokenMalformed(MagicError):
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class DIDTokenExpired(MagicError):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ExpectedBearerStringError(MagicError):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class APIConnectionError(MagicError):
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class RequestError(MagicError):
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
message=None,
|
|
43
|
+
http_status=None,
|
|
44
|
+
http_code=None,
|
|
45
|
+
http_resp_data=None,
|
|
46
|
+
http_message=None,
|
|
47
|
+
http_error_code=None,
|
|
48
|
+
http_request_params=None,
|
|
49
|
+
http_request_data=None,
|
|
50
|
+
http_method=None,
|
|
51
|
+
):
|
|
52
|
+
super().__init__(message)
|
|
53
|
+
self.http_status = http_status
|
|
54
|
+
self.http_code = http_code
|
|
55
|
+
self.http_resp_data = http_resp_data
|
|
56
|
+
self.http_message = http_message
|
|
57
|
+
self.http_error_code = http_error_code
|
|
58
|
+
self.http_request_params = http_request_params
|
|
59
|
+
self.http_request_data = http_request_data
|
|
60
|
+
self.http_method = http_method
|
|
61
|
+
|
|
62
|
+
def __repr__(self):
|
|
63
|
+
return (
|
|
64
|
+
"{error_class}(message={message!r}, "
|
|
65
|
+
"http_error_code={http_error_code}, "
|
|
66
|
+
"http_code={http_code}).".format(
|
|
67
|
+
error_class=self.__class__.__name__,
|
|
68
|
+
message=self._message or None,
|
|
69
|
+
http_error_code=self.http_error_code or None,
|
|
70
|
+
http_code=self.http_code or None,
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def to_dict(self):
|
|
75
|
+
_dict = super().to_dict()
|
|
76
|
+
for attr in self.__dict__:
|
|
77
|
+
if attr.startswith("http_"):
|
|
78
|
+
_dict[attr] = self.__dict__[attr]
|
|
79
|
+
|
|
80
|
+
return _dict
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class RateLimitingError(RequestError):
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class BadRequestError(RequestError):
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class AuthenticationError(RequestError):
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class ForbiddenError(RequestError):
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class APIError(RequestError):
|
|
100
|
+
pass
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import platform
|
|
3
|
+
|
|
4
|
+
from requests import Session
|
|
5
|
+
from requests.adapters import HTTPAdapter
|
|
6
|
+
from requests.packages.urllib3.util.retry import Retry
|
|
7
|
+
|
|
8
|
+
import magic_admin
|
|
9
|
+
from magic_admin import version
|
|
10
|
+
from magic_admin.config import api_secret_api_key_missing_message
|
|
11
|
+
from magic_admin.config import base_url
|
|
12
|
+
from magic_admin.error import APIConnectionError
|
|
13
|
+
from magic_admin.error import APIError
|
|
14
|
+
from magic_admin.error import AuthenticationError
|
|
15
|
+
from magic_admin.error import BadRequestError
|
|
16
|
+
from magic_admin.error import ForbiddenError
|
|
17
|
+
from magic_admin.error import RateLimitingError
|
|
18
|
+
from magic_admin.response import MagicResponse
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class RequestsClient:
|
|
22
|
+
def __init__(self, retries, timeout, backoff_factor):
|
|
23
|
+
self._retries = retries
|
|
24
|
+
self._timeout = timeout
|
|
25
|
+
self._backoff_factor = backoff_factor
|
|
26
|
+
|
|
27
|
+
self._setup_request_session()
|
|
28
|
+
|
|
29
|
+
@staticmethod
|
|
30
|
+
def _get_platform_info():
|
|
31
|
+
platform_info = {}
|
|
32
|
+
|
|
33
|
+
for attr, func in [
|
|
34
|
+
["platform", platform.platform],
|
|
35
|
+
["language_version", platform.python_version],
|
|
36
|
+
["uname", platform.uname],
|
|
37
|
+
]:
|
|
38
|
+
try:
|
|
39
|
+
val = str(func())
|
|
40
|
+
except Exception as e:
|
|
41
|
+
val = "<{}>".format(str(e))
|
|
42
|
+
|
|
43
|
+
platform_info[attr] = val
|
|
44
|
+
|
|
45
|
+
return platform_info
|
|
46
|
+
|
|
47
|
+
def _setup_request_session(self):
|
|
48
|
+
"""Take advantage of the ``requests.Session``. If client is making several
|
|
49
|
+
requests to the same host, the underlying TCP connection will be reused,
|
|
50
|
+
which can result in a significant performance increase.
|
|
51
|
+
"""
|
|
52
|
+
self.http = Session()
|
|
53
|
+
self.http.mount(
|
|
54
|
+
base_url,
|
|
55
|
+
HTTPAdapter(
|
|
56
|
+
max_retries=Retry(
|
|
57
|
+
total=self._retries,
|
|
58
|
+
backoff_factor=self._backoff_factor,
|
|
59
|
+
),
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def _get_request_headers(self):
|
|
64
|
+
user_agent = {
|
|
65
|
+
"language": "python",
|
|
66
|
+
"sdk_version": version.VERSION,
|
|
67
|
+
"publisher": "magic",
|
|
68
|
+
"http_lib": self.__class__.__name__,
|
|
69
|
+
**self._get_platform_info(),
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if magic_admin.api_secret_key is None:
|
|
73
|
+
raise AuthenticationError(api_secret_api_key_missing_message)
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
"X-Magic-Secret-Key": magic_admin.api_secret_key,
|
|
77
|
+
"User-Agent": json.dumps(user_agent),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
def request(self, method, url, params=None, data=None):
|
|
81
|
+
try:
|
|
82
|
+
api_resp = self.http.request(
|
|
83
|
+
method,
|
|
84
|
+
url,
|
|
85
|
+
params=params,
|
|
86
|
+
# Requests auto-converts this to JSON and add content-type
|
|
87
|
+
# `application/json`.
|
|
88
|
+
json=data,
|
|
89
|
+
headers=self._get_request_headers(),
|
|
90
|
+
timeout=self._timeout,
|
|
91
|
+
)
|
|
92
|
+
except Exception as e:
|
|
93
|
+
return self._handle_request_error(e)
|
|
94
|
+
|
|
95
|
+
return self._parse_and_convert_to_api_response(
|
|
96
|
+
api_resp,
|
|
97
|
+
params,
|
|
98
|
+
data,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def _parse_and_convert_to_api_response(self, resp, request_params, request_data):
|
|
102
|
+
status_code = resp.status_code
|
|
103
|
+
|
|
104
|
+
if 200 <= status_code < 300:
|
|
105
|
+
return MagicResponse(resp.content, resp.json(), status_code)
|
|
106
|
+
|
|
107
|
+
if status_code == 429:
|
|
108
|
+
error_class = RateLimitingError
|
|
109
|
+
elif status_code == 400:
|
|
110
|
+
error_class = BadRequestError
|
|
111
|
+
elif status_code == 401:
|
|
112
|
+
error_class = AuthenticationError
|
|
113
|
+
elif status_code == 403:
|
|
114
|
+
error_class = ForbiddenError
|
|
115
|
+
else:
|
|
116
|
+
error_class = APIError
|
|
117
|
+
|
|
118
|
+
resp_data = resp.json()
|
|
119
|
+
raise error_class(
|
|
120
|
+
http_status=resp_data.get("status"),
|
|
121
|
+
http_code=status_code,
|
|
122
|
+
http_resp_data=resp_data.get("data"),
|
|
123
|
+
http_message=resp_data.get("message"),
|
|
124
|
+
http_error_code=resp_data.get("error_code"),
|
|
125
|
+
http_request_params=request_params,
|
|
126
|
+
http_request_data=request_data,
|
|
127
|
+
http_method=resp.request.method,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def _handle_request_error(self, e):
|
|
131
|
+
message = (
|
|
132
|
+
"Unexpected error thrown while communicating to Magic. "
|
|
133
|
+
"Please reach out to support@magic.link if the problem continues. "
|
|
134
|
+
"Error message: {error_class} was raised - {error_message}".format(
|
|
135
|
+
error_class=e.__class__.__name__,
|
|
136
|
+
error_message=str(e) or "no error message.",
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
raise APIConnectionError(message)
|
magic_admin/magic.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import magic_admin
|
|
4
|
+
from magic_admin.config import api_secret_api_key_missing_message
|
|
5
|
+
from magic_admin.config import base_url
|
|
6
|
+
from magic_admin.error import AuthenticationError
|
|
7
|
+
from magic_admin.http_client import RequestsClient
|
|
8
|
+
from magic_admin.resources.base import ResourceComponent
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
RETRIES = 3
|
|
12
|
+
TIMEOUT = 10
|
|
13
|
+
BACKOFF_FACTOR = 0.02
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Magic:
|
|
17
|
+
v1_client_info = base_url + "/v1/admin/client"
|
|
18
|
+
|
|
19
|
+
def __getattr__(self, attribute_name):
|
|
20
|
+
try:
|
|
21
|
+
return getattr(self._resource, attribute_name)
|
|
22
|
+
except AttributeError:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
return super().__getattribute__(attribute_name)
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
api_secret_key=None,
|
|
30
|
+
client_id=None,
|
|
31
|
+
retries=RETRIES,
|
|
32
|
+
timeout=TIMEOUT,
|
|
33
|
+
backoff_factor=BACKOFF_FACTOR,
|
|
34
|
+
):
|
|
35
|
+
self._resource = ResourceComponent()
|
|
36
|
+
|
|
37
|
+
self._resource.setup_request_client(retries, timeout, backoff_factor)
|
|
38
|
+
self._set_api_secret_key(api_secret_key)
|
|
39
|
+
init_requests_client = RequestsClient(retries, timeout, backoff_factor)
|
|
40
|
+
magic_admin.client_id = (
|
|
41
|
+
client_id
|
|
42
|
+
or init_requests_client.request("get", self.v1_client_info).data[
|
|
43
|
+
"client_id"
|
|
44
|
+
]
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def _set_api_secret_key(self, api_secret_key):
|
|
48
|
+
magic_admin.api_secret_key = api_secret_key or os.environ.get(
|
|
49
|
+
"MAGIC_API_SECRET_KEY",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if magic_admin.api_secret_key is None:
|
|
53
|
+
raise AuthenticationError(api_secret_api_key_missing_message)
|
magic_admin/response.py
ADDED
magic_admin/version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "2.1.2"
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: magic-admin
|
|
3
|
+
Version: 2.1.2
|
|
4
|
+
Summary: Magic Python Library
|
|
5
|
+
Author-email: Magic <support@magic.link>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Website, https://magic.link
|
|
8
|
+
Keywords: magic,python,sdk
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Programming Language :: Python
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.11
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: requests==2.32.5
|
|
17
|
+
Requires-Dist: web3==7.13.0
|
|
18
|
+
Requires-Dist: websockets==15.0.1
|
|
19
|
+
|
|
20
|
+
# Magic Admin Python SDK
|
|
21
|
+
|
|
22
|
+
[](https://www.python.org/downloads/)
|
|
23
|
+
[](https://badge.fury.io/py/magic-admin)
|
|
24
|
+
[](LICENSE.txt)
|
|
25
|
+
|
|
26
|
+
The Magic Admin Python SDK provides a simple and powerful way to integrate Magic's authentication system into your Python applications. Handle [DID Tokens](https://docs.magic.link/embedded-wallets/authentication/features/decentralized-id#decentralized-id-did-tokens) and interact with Magic API endpoints with ease.
|
|
27
|
+
|
|
28
|
+
## 📚 Documentation
|
|
29
|
+
|
|
30
|
+
📖 **Full Documentation**: [Magic Python SDK Docs](https://docs.magic.link/embedded-wallets/sdk/server-side/python)
|
|
31
|
+
|
|
32
|
+
## 🚀 Quick Start
|
|
33
|
+
|
|
34
|
+
### Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Using pip
|
|
38
|
+
pip install magic-admin
|
|
39
|
+
|
|
40
|
+
# Using conda
|
|
41
|
+
conda install magic-admin
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Basic Usage
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from magic_admin import Magic
|
|
48
|
+
|
|
49
|
+
# Initialize with your API secret key
|
|
50
|
+
magic = Magic(api_secret_key='your_api_secret_key_here')
|
|
51
|
+
|
|
52
|
+
# Validate a DID token
|
|
53
|
+
try:
|
|
54
|
+
magic.Token.validate('DID_TOKEN_FROM_CLIENT')
|
|
55
|
+
print("Token is valid!")
|
|
56
|
+
except Exception as e:
|
|
57
|
+
print(f"Token validation failed: {e}")
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Environment Variable Configuration
|
|
61
|
+
|
|
62
|
+
You can also load your API secret key from an environment variable:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
export MAGIC_API_SECRET_KEY="your_api_secret_key_here"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from magic_admin import Magic
|
|
70
|
+
|
|
71
|
+
# Automatically uses MAGIC_API_SECRET_KEY environment variable
|
|
72
|
+
magic = Magic()
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
> **Note**: The API secret key passed directly to `Magic()` takes precedence over the environment variable.
|
|
76
|
+
|
|
77
|
+
### Network Configuration
|
|
78
|
+
|
|
79
|
+
Customize network behavior for your application:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
magic = Magic(
|
|
83
|
+
api_secret_key='your_key',
|
|
84
|
+
retries=5, # Number of retry attempts
|
|
85
|
+
timeout=10, # Request timeout in seconds
|
|
86
|
+
backoff_factor=0.03 # Exponential backoff factor
|
|
87
|
+
)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## 🔧 Development
|
|
91
|
+
|
|
92
|
+
### Prerequisites
|
|
93
|
+
|
|
94
|
+
- Python 3.11+
|
|
95
|
+
- Git
|
|
96
|
+
|
|
97
|
+
### Setup Development Environment
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Clone the repository
|
|
101
|
+
git clone https://github.com/magiclabs/magic-admin-python.git
|
|
102
|
+
cd magic-admin-python
|
|
103
|
+
|
|
104
|
+
# Create virtual environment and install dependencies
|
|
105
|
+
make development
|
|
106
|
+
|
|
107
|
+
# Activate the virtual environment
|
|
108
|
+
source virtualenv_run/bin/activate
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Running Tests
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Run tests against all supported Python versions (3.11, 3.12, 3.13)
|
|
115
|
+
make test
|
|
116
|
+
|
|
117
|
+
# Run tests with coverage
|
|
118
|
+
make test
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Code Quality
|
|
122
|
+
|
|
123
|
+
This project uses [pre-commit](https://pre-commit.com/) to maintain code quality. Hooks run automatically on every commit.
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Run pre-commit hooks manually
|
|
127
|
+
pre-commit run --all-files
|
|
128
|
+
|
|
129
|
+
# Install pre-commit hooks
|
|
130
|
+
pre-commit install
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Cleanup
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Remove virtual environment, tox logs, and pytest cache
|
|
137
|
+
make clean
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## 📋 Requirements
|
|
141
|
+
|
|
142
|
+
- **Python**: 3.11+
|
|
143
|
+
- **Dependencies**: See [requirements.txt](requirements.txt)
|
|
144
|
+
- **Development**: See [requirements-dev.txt](requirements-dev.txt)
|
|
145
|
+
|
|
146
|
+
## 🤝 Contributing
|
|
147
|
+
|
|
148
|
+
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
149
|
+
|
|
150
|
+
### Development Workflow
|
|
151
|
+
|
|
152
|
+
1. Fork the repository
|
|
153
|
+
2. Create a feature branch
|
|
154
|
+
3. Make your changes
|
|
155
|
+
4. Run tests: `make test`
|
|
156
|
+
5. Run pre-commit: `pre-commit run --all-files`
|
|
157
|
+
6. Submit a pull request
|
|
158
|
+
|
|
159
|
+
## 📄 License
|
|
160
|
+
|
|
161
|
+
This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details.
|
|
162
|
+
|
|
163
|
+
## 📝 Changelog
|
|
164
|
+
|
|
165
|
+
See [CHANGELOG.md](CHANGELOG.md) for a detailed history of changes.
|
|
166
|
+
|
|
167
|
+
## 🔗 Links
|
|
168
|
+
|
|
169
|
+
- [Magic Documentation](https://docs.magic.link)
|
|
170
|
+
- [Magic Dashboard](https://dashboard.magic.link)
|
|
171
|
+
- [Magic Python SDK Docs](https://docs.magic.link/embedded-wallets/sdk/server-side/python)
|
|
172
|
+
- [DID Token Documentation](https://docs.magic.link/embedded-wallets/authentication/features/decentralized-id#decentralized-id-did-tokens)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
magic_admin/__init__.py,sha256=DuRZ7GbiA_ZmOZ-9eOjTMvRDC5JuVE-4F7ZG4IoGfG4,213
|
|
2
|
+
magic_admin/config.py,sha256=n2HFDIxDTRRQTOYwLspb4lGUnObQTwK3gK5_kohWNDs,456
|
|
3
|
+
magic_admin/error.py,sha256=73yr-TWgxGY5kvMMrtPJKzP566HI41MZ_hVd6OcrFO0,2270
|
|
4
|
+
magic_admin/http_client.py,sha256=9Sg5rLhIZDfVo-xj9agkuqOOR_xQqHxLAAhg0fdlXJk,4570
|
|
5
|
+
magic_admin/magic.py,sha256=-ydXF1ALw4l53bWVZPuZRw-LIL-9LEIyaitggdd2AHw,1542
|
|
6
|
+
magic_admin/response.py,sha256=irjZDDLv79IzmIK_sohcZw-1UAbDZWsDjccKv0kujO8,178
|
|
7
|
+
magic_admin/version.py,sha256=g3cxz98sq2sH94EJfsXt9vgetLlENXp3IEKtEY6wMyg,18
|
|
8
|
+
magic_admin-2.1.2.dist-info/METADATA,sha256=e_IHIQzzeeQ6A-m1Vxg4fOdMcuU61Y03lx7CPo2V6m4,4433
|
|
9
|
+
magic_admin-2.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
magic_admin-2.1.2.dist-info/top_level.txt,sha256=apXKs0D-XDoIjenh62w68eONw8qHW7s5FgRoA5vc2aw,12
|
|
11
|
+
magic_admin-2.1.2.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
magic_admin
|