fprime-gds 3.4.0__py3-none-any.whl → 3.4.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.
@@ -1,542 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- """
3
- flaskext.uploads
4
- ================
5
- This module provides upload support for Flask. The basic pattern is to set up
6
- an `UploadSet` object and upload your files to it.
7
-
8
- :copyright: 2010 Matthew "LeafStorm" Frazier
9
- :license: MIT/X11, see LICENSE for details
10
-
11
- Note: originally from https://github.com/maxcountryman/flask-uploads
12
- """
13
-
14
- import sys
15
-
16
- PY3 = sys.version_info[0] == 3
17
-
18
- if PY3:
19
- string_types = (str,)
20
- else:
21
- string_types = (basestring,)
22
-
23
- import os.path
24
- import posixpath
25
- from itertools import chain # lgtm [py/unused-import]
26
-
27
- from flask import Blueprint, abort, current_app, send_from_directory, url_for
28
- from werkzeug.datastructures import FileStorage
29
- from werkzeug.utils import secure_filename
30
-
31
- # Extension presets
32
-
33
- #: This just contains plain text files (.txt).
34
- TEXT = ("txt",)
35
-
36
- #: This contains various office document formats (.rtf, .odf, .ods, .gnumeric,
37
- #: .abw, .doc, .docx, .xls, .xlsx and .pdf). Note that the macro-enabled versions
38
- #: of Microsoft Office 2007 files are not included.
39
- DOCUMENTS = tuple("rtf odf ods gnumeric abw doc docx xls xlsx pdf".split())
40
-
41
- #: This contains basic image types that are viewable from most browsers (.jpg,
42
- #: .jpe, .jpeg, .png, .gif, .svg, .bmp and .webp).
43
- IMAGES = tuple("jpg jpe jpeg png gif svg bmp webp".split())
44
-
45
- #: This contains audio file types (.wav, .mp3, .aac, .ogg, .oga, and .flac).
46
- AUDIO = tuple("wav mp3 aac ogg oga flac".split())
47
-
48
- #: This is for structured data files (.csv, .ini, .json, .plist, .xml, .yaml,
49
- #: and .yml).
50
- DATA = tuple("csv ini json plist xml yaml yml".split())
51
-
52
- #: This contains various types of scripts (.js, .php, .pl, .py .rb, and .sh).
53
- #: If your Web server has PHP installed and set to auto-run, you might want to
54
- #: add ``php`` to the DENY setting.
55
- SCRIPTS = tuple("js php pl py rb sh".split())
56
-
57
- #: This contains archive and compression formats (.gz, .bz2, .zip, .tar,
58
- #: .tgz, .txz, and .7z).
59
- ARCHIVES = tuple("gz bz2 zip tar tgz txz 7z".split())
60
-
61
- #: This contains nonexecutable source files - those which need to be
62
- #: compiled or assembled to binaries to be used. They are generally safe to
63
- #: accept, as without an existing RCE vulnerability, they cannot be compiled,
64
- #: assembled, linked, or executed. Supports C, C++, Ada, Rust, Go (Golang),
65
- #: FORTRAN, D, Java, C Sharp, F Sharp (compiled only), COBOL, Haskell, and
66
- #: assembly.
67
- SOURCE = tuple(
68
- (
69
- "c cpp c++ h hpp h++ cxx hxx hdl " # C/C++
70
- + "ada " # Ada
71
- + "rs " # Rust
72
- + "go " # Go
73
- + "f for f90 f95 f03 " # FORTRAN
74
- + "d dd di " # D
75
- + "java " # Java
76
- + "hs " # Haskell
77
- + "cs " # C Sharp
78
- + "fs " # F Sharp compiled source (NOT .fsx, which is interactive-ready)
79
- + "cbl cob " # COBOL
80
- + "asm s " # Assembly
81
- ).split()
82
- )
83
-
84
- #: This contains shared libraries and executable files (.so, .exe and .dll).
85
- #: Most of the time, you will not want to allow this - it's better suited for
86
- #: use with `AllExcept`.
87
- EXECUTABLES = tuple("so exe dll".split())
88
-
89
- #: The default allowed extensions - `TEXT`, `DOCUMENTS`, `DATA`, and `IMAGES`.
90
- DEFAULTS = TEXT + DOCUMENTS + IMAGES + DATA
91
-
92
-
93
- class UploadNotAllowed(Exception):
94
- """
95
- This exception is raised if the upload was not allowed. You should catch
96
- it in your view code and display an appropriate message to the user.
97
- """
98
-
99
-
100
- def tuple_from(*iters):
101
- return tuple(itertools.chain(*iters))
102
-
103
-
104
- def extension(filename):
105
- ext = os.path.splitext(filename)[1]
106
- if ext == "":
107
- # add non-ascii filename support
108
- ext = os.path.splitext(filename)[0]
109
- if ext.startswith("."):
110
- # os.path.splitext retains . separator
111
- ext = ext[1:]
112
- return ext
113
-
114
-
115
- def lowercase_ext(filename):
116
- """
117
- This is a helper used by UploadSet.save to provide lowercase extensions for
118
- all processed files, to compare with configured extensions in the same
119
- case.
120
-
121
- .. versionchanged:: 0.1.4
122
- Filenames without extensions are no longer lowercased, only the
123
- extension is returned in lowercase, if an extension exists.
124
-
125
- :param filename: The filename to ensure has a lowercase extension.
126
- """
127
- if "." in filename:
128
- main, ext = os.path.splitext(filename)
129
- return main + ext.lower()
130
- # For consistency with os.path.splitext,
131
- # do not treat a filename without an extension as an extension.
132
- # That is, do not return filename.lower().
133
- return filename
134
-
135
-
136
- def addslash(url):
137
- return url if url.endswith("/") else f"{url}/"
138
-
139
-
140
- def patch_request_class(app, size=64 * 1024 * 1024):
141
- """
142
- By default, Flask will accept uploads to an arbitrary size. While Werkzeug
143
- switches uploads from memory to a temporary file when they hit 500 KiB,
144
- it's still possible for someone to overload your disk space with a
145
- gigantic file.
146
-
147
- This patches the app's request class's
148
- `~werkzeug.BaseRequest.max_content_length` attribute so that any upload
149
- larger than the given size is rejected with an HTTP error.
150
-
151
- .. note::
152
-
153
- In Flask 0.6, you can do this by setting the `MAX_CONTENT_LENGTH`
154
- setting, without patching the request class. To emulate this behavior,
155
- you can pass `None` as the size (you must pass it explicitly). That is
156
- the best way to call this function, as it won't break the Flask 0.6
157
- functionality if it exists.
158
-
159
- .. versionchanged:: 0.1.1
160
-
161
- :param app: The app to patch the request class of.
162
- :param size: The maximum size to accept, in bytes. The default is 64 MiB.
163
- If it is `None`, the app's `MAX_CONTENT_LENGTH` configuration
164
- setting will be used to patch.
165
- """
166
- if size is None:
167
- if isinstance(app.request_class.__dict__["max_content_length"], property):
168
- return
169
- size = app.config.get("MAX_CONTENT_LENGTH")
170
- reqclass = app.request_class
171
- patched = type(reqclass.__name__, (reqclass,), {"max_content_length": size})
172
- app.request_class = patched
173
-
174
-
175
- def config_for_set(uset, app, defaults=None):
176
- """
177
- This is a helper function for `configure_uploads` that extracts the
178
- configuration for a single set.
179
-
180
- :param uset: The upload set.
181
- :param app: The app to load the configuration from.
182
- :param defaults: A dict with keys `url` and `dest` from the
183
- `UPLOADS_DEFAULT_DEST` and `DEFAULT_UPLOADS_URL`
184
- settings.
185
- """
186
- config = app.config
187
- prefix = f'UPLOADED_{uset.name.upper()}_'
188
- using_defaults = False
189
- if defaults is None:
190
- defaults = dict(dest=None, url=None)
191
-
192
- allow_extns = tuple(config.get(prefix + "ALLOW", ()))
193
- deny_extns = tuple(config.get(prefix + "DENY", ()))
194
- destination = config.get(prefix + "DEST")
195
- base_url = config.get(prefix + "URL")
196
-
197
- if destination is None:
198
- # the upload set's destination wasn't given
199
- if uset.default_dest:
200
- # use the "default_dest" callable
201
- destination = uset.default_dest(app)
202
- if destination is None: # still
203
- # use the default dest from the config
204
- if defaults["dest"] is not None:
205
- using_defaults = True
206
- destination = os.path.join(defaults["dest"], uset.name)
207
- else:
208
- msg = f"no destination for set {uset.name}"
209
- raise RuntimeError(msg)
210
-
211
- if base_url is None and using_defaults and defaults["url"]:
212
- base_url = addslash(defaults["url"]) + uset.name + "/"
213
-
214
- return UploadConfiguration(destination, base_url, allow_extns, deny_extns)
215
-
216
-
217
- def configure_uploads(app, upload_sets):
218
- """
219
- Call this after the app has been configured. It will go through all the
220
- upload sets, get their configuration, and store the configuration on the
221
- app. It will also register the uploads module if it hasn't been set. This
222
- can be called multiple times with different upload sets.
223
-
224
- .. versionchanged:: 0.1.3
225
- The uploads module/blueprint will only be registered if it is needed
226
- to serve the upload sets.
227
-
228
- :param app: The `~flask.Flask` instance to get the configuration from.
229
- :param upload_sets: The `UploadSet` instances to configure.
230
- """
231
- if isinstance(upload_sets, UploadSet):
232
- upload_sets = (upload_sets,)
233
-
234
- if not hasattr(app, "upload_set_config"):
235
- app.upload_set_config = {}
236
- set_config = app.upload_set_config
237
- defaults = dict(
238
- dest=app.config.get("UPLOADS_DEFAULT_DEST"),
239
- url=app.config.get("UPLOADS_DEFAULT_URL"),
240
- )
241
-
242
- for uset in upload_sets:
243
- config = config_for_set(uset, app, defaults)
244
- set_config[uset.name] = config
245
-
246
- should_serve = any(s.base_url is None for s in set_config.values())
247
- if "_uploads" not in app.blueprints and should_serve:
248
- app.register_blueprint(uploads_mod)
249
-
250
-
251
- class All(object):
252
- """
253
- This type can be used to allow all extensions. There is a predefined
254
- instance named `ALL`.
255
- """
256
-
257
- def __contains__(self, item):
258
- return True
259
-
260
-
261
- #: This "contains" all items. You can use it to allow all extensions to be
262
- #: uploaded.
263
- ALL = All()
264
-
265
-
266
- class AllExcept(object):
267
- """
268
- This can be used to allow all file types except certain ones. For example,
269
- to ban .exe and .iso files, pass::
270
-
271
- AllExcept(('exe', 'iso'))
272
-
273
- to the `UploadSet` constructor as `extensions`. You can use any container,
274
- for example::
275
-
276
- AllExcept(SCRIPTS + EXECUTABLES)
277
- """
278
-
279
- def __init__(self, items):
280
- self.items = items
281
-
282
- def __contains__(self, item):
283
- return item not in self.items
284
-
285
-
286
- class UploadConfiguration(object):
287
- """
288
- This holds the configuration for a single `UploadSet`. The constructor's
289
- arguments are also the attributes.
290
-
291
- :param destination: The directory to save files to.
292
- :param base_url: The URL (ending with a /) that files can be downloaded
293
- from. If this is `None`, Flask-Uploads will serve the
294
- files itself.
295
- :param allow: A list of extensions to allow, even if they're not in the
296
- `UploadSet` extensions list.
297
- :param deny: A list of extensions to deny, even if they are in the
298
- `UploadSet` extensions list.
299
- """
300
-
301
- def __init__(self, destination, base_url=None, allow=(), deny=()):
302
- self.destination = destination
303
- self.base_url = base_url
304
- self.allow = allow
305
- self.deny = deny
306
-
307
- @property
308
- def tuple(self):
309
- return (self.destination, self.base_url, self.allow, self.deny)
310
-
311
- def __eq__(self, other):
312
- return self.tuple == other.tuple
313
-
314
-
315
- class UploadSet(object):
316
- """
317
- This represents a single set of uploaded files. Each upload set is
318
- independent of the others. This can be reused across multiple application
319
- instances, as all configuration is stored on the application object itself
320
- and found with `flask.current_app`.
321
-
322
- :param name: The name of this upload set. It defaults to ``files``, but
323
- you can pick any alphanumeric name you want. (For simplicity,
324
- it's best to use a plural noun.)
325
- :param extensions: The extensions to allow uploading in this set. The
326
- easiest way to do this is to add together the extension
327
- presets (for example, ``TEXT + DOCUMENTS + IMAGES``).
328
- It can be overridden by the configuration with the
329
- `UPLOADED_X_ALLOW` and `UPLOADED_X_DENY` configuration
330
- parameters. The default is `DEFAULTS`.
331
- :param default_dest: If given, this should be a callable. If you call it
332
- with the app, it should return the default upload
333
- destination path for that app.
334
- """
335
-
336
- def __init__(self, name="files", extensions=DEFAULTS, default_dest=None):
337
- if not name.isalnum():
338
- raise ValueError("Name must be alphanumeric (no underscores)")
339
- self.name = name
340
- self.extensions = extensions
341
- self._config = None
342
- self.default_dest = default_dest
343
-
344
- @property
345
- def config(self):
346
- """
347
- This gets the current configuration. By default, it looks up the
348
- current application and gets the configuration from there. But if you
349
- don't want to go to the full effort of setting an application, or it's
350
- otherwise outside of a request context, set the `_config` attribute to
351
- an `UploadConfiguration` instance, then set it back to `None` when
352
- you're done.
353
- """
354
- if self._config is not None:
355
- return self._config
356
- try:
357
- return current_app.upload_set_config[self.name]
358
- except AttributeError:
359
- raise RuntimeError("cannot access configuration outside request")
360
-
361
- def url(self, filename):
362
- """
363
- This function gets the URL a file uploaded to this set would be
364
- accessed at. It doesn't check whether said file exists.
365
-
366
- :param filename: The filename to return the URL for.
367
- """
368
- base = self.config.base_url
369
- if base is None:
370
- return url_for(
371
- "_uploads.uploaded_file",
372
- setname=self.name,
373
- filename=filename,
374
- _external=True,
375
- )
376
- return base + filename
377
-
378
- def path(self, filename, folder=None):
379
- """
380
- This returns the absolute path of a file uploaded to this set. It
381
- doesn't actually check whether said file exists.
382
-
383
- :param filename: The filename to return the path for.
384
- :param folder: The subfolder within the upload set previously used
385
- to save to.
386
- """
387
- if folder is not None:
388
- target_folder = os.path.join(self.config.destination, folder)
389
- else:
390
- target_folder = self.config.destination
391
- return os.path.join(target_folder, filename)
392
-
393
- def file_allowed(self, storage, basename):
394
- """
395
- This tells whether a file is allowed. It should return `True` if the
396
- given `werkzeug.FileStorage` object can be saved with the given
397
- basename, and `False` if it can't. The default implementation just
398
- checks the extension, so you can override this if you want.
399
-
400
- :param storage: The `werkzeug.FileStorage` to check.
401
- :param basename: The basename it will be saved under.
402
- """
403
- return self.extension_allowed(extension(basename))
404
-
405
- def extension_allowed(self, ext):
406
- """
407
- This determines whether a specific extension is allowed. It is called
408
- by `file_allowed`, so if you override that but still want to check
409
- extensions, call back into this.
410
-
411
- :param ext: The extension to check, without the dot.
412
- """
413
- return (ext in self.config.allow) or (
414
- ext in self.extensions and ext not in self.config.deny
415
- )
416
-
417
- def get_basename(self, filename):
418
- return lowercase_ext(secure_filename(filename))
419
-
420
- def save(self, storage, folder=None, name=None):
421
- """
422
- This saves a `werkzeug.FileStorage` into this upload set. If the
423
- upload is not allowed, an `UploadNotAllowed` error will be raised.
424
- Otherwise, the file will be saved and its name (including the folder)
425
- will be returned.
426
-
427
- :param storage: The uploaded file to save.
428
- :param folder: The subfolder within the upload set to save to.
429
- :param name: The name to save the file as. If it ends with a dot, the
430
- file's extension will be appended to the end. (If you
431
- are using `name`, you can include the folder in the
432
- `name` instead of explicitly using `folder`, i.e.
433
- ``uset.save(file, name="someguy/photo_123.")``
434
- """
435
- if not isinstance(storage, FileStorage):
436
- raise TypeError("storage must be a werkzeug.FileStorage")
437
-
438
- if folder is None and name is not None and "/" in name:
439
- folder, name = os.path.split(name)
440
-
441
- basename = self.get_basename(storage.filename)
442
-
443
- if not self.file_allowed(storage, basename):
444
- raise UploadNotAllowed()
445
-
446
- if name:
447
- basename = name + extension(basename) if name.endswith(".") else name
448
-
449
- if folder:
450
- target_folder = os.path.join(self.config.destination, folder)
451
- else:
452
- target_folder = self.config.destination
453
- if not os.path.exists(target_folder):
454
- os.makedirs(target_folder)
455
- if os.path.exists(os.path.join(target_folder, basename)):
456
- basename = self.resolve_conflict(target_folder, basename)
457
-
458
- target = os.path.join(target_folder, basename)
459
- storage.save(target)
460
- return posixpath.join(folder, basename) if folder else basename
461
-
462
- def resolve_conflict(self, target_folder, basename):
463
- """
464
- If a file with the selected name already exists in the target folder,
465
- this method is called to resolve the conflict. It should return a new
466
- basename for the file.
467
-
468
- The default implementation splits the name and extension and adds a
469
- suffix to the name consisting of an underscore and a number, and tries
470
- that until it finds one that doesn't exist.
471
-
472
- :param target_folder: The absolute path to the target.
473
- :param basename: The file's original basename.
474
- """
475
- name, ext = os.path.splitext(basename)
476
- count = 0
477
- while True:
478
- count = count + 1
479
- newname = "%s_%d%s" % (name, count, ext)
480
- if not os.path.exists(os.path.join(target_folder, newname)):
481
- return newname
482
-
483
-
484
- uploads_mod = Blueprint("_uploads", __name__, url_prefix="/_uploads")
485
-
486
-
487
- @uploads_mod.route("/<setname>/<path:filename>")
488
- def uploaded_file(setname, filename):
489
- config = current_app.upload_set_config.get(setname)
490
- if config is None:
491
- abort(404)
492
- return send_from_directory(config.destination, filename)
493
-
494
-
495
- class TestingFileStorage(FileStorage):
496
- """
497
- This is a helper for testing upload behavior in your application. You
498
- can manually create it, and its save method is overloaded to set `saved`
499
- to the name of the file it was saved to. All of these parameters are
500
- optional, so only bother setting the ones relevant to your application.
501
-
502
- :param stream: A stream. The default is an empty stream.
503
- :param filename: The filename uploaded from the client. The default is the
504
- stream's name.
505
- :param name: The name of the form field it was loaded from. The default is
506
- `None`.
507
- :param content_type: The content type it was uploaded as. The default is
508
- ``application/octet-stream``.
509
- :param content_length: How long it is. The default is -1.
510
- :param headers: Multipart headers as a `werkzeug.Headers`. The default is
511
- `None`.
512
- """
513
-
514
- def __init__(
515
- self,
516
- stream=None,
517
- filename=None,
518
- name=None,
519
- content_type="application/octet-stream",
520
- content_length=-1,
521
- headers=None,
522
- ):
523
- FileStorage.__init__(
524
- self,
525
- stream,
526
- filename,
527
- name=name,
528
- content_type=content_type,
529
- content_length=content_length,
530
- headers=None,
531
- )
532
- self.saved = None
533
-
534
- def save(self, dst, buffer_size=16384):
535
- """
536
- This marks the file as saved by setting the `saved` attribute to the
537
- name of the file it was saved to.
538
-
539
- :param dst: The file to save to.
540
- :param buffer_size: Ignored.
541
- """
542
- self.saved = dst if isinstance(dst, string_types) else dst.name
@@ -1,42 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: fprime-gds
3
- Version: 3.4.0
4
- Summary: F Prime Flight Software Ground Data System layer.
5
- Home-page: https://github.com/nasa/fprime
6
- Author: Michael Starch
7
- Author-email: Michael.D.Starch@jpl.nasa.gov
8
- License: Apache 2.0 License
9
- Project-URL: Issue Tracker, https://github.com/nasa/fprime/issues
10
- Keywords: fprime,gds,embedded,nasa
11
- Classifier: Development Status :: 5 - Production/Stable
12
- Classifier: Intended Audience :: Developers
13
- Classifier: Operating System :: Unix
14
- Classifier: Operating System :: POSIX
15
- Classifier: Programming Language :: Python
16
- Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.7
18
- Classifier: Programming Language :: Python :: 3.8
19
- Classifier: Programming Language :: Python :: 3.9
20
- Classifier: Programming Language :: Python :: 3.10
21
- Classifier: Programming Language :: Python :: Implementation :: CPython
22
- Classifier: Programming Language :: Python :: Implementation :: PyPy
23
- Requires-Python: >=3.7
24
- License-File: LICENSE.txt
25
- License-File: NOTICE.txt
26
- Requires-Dist: flask >=3.0.0
27
- Requires-Dist: flask-compress >=1.11
28
- Requires-Dist: pyzmq >=24.0.1
29
- Requires-Dist: pexpect >=4.8.0
30
- Requires-Dist: pytest >=6.2.4
31
- Requires-Dist: flask-restful >=0.3.8
32
- Requires-Dist: fprime-tools >=3.1.2a1
33
- Requires-Dist: argcomplete >=1.12.3
34
- Requires-Dist: Jinja2 >=2.11.3
35
- Requires-Dist: openpyxl >=3.0.10
36
- Requires-Dist: pyserial >=3.5
37
-
38
-
39
- This package contains the Python files used to run the F prime Ground Data System and Test API.
40
- It is intended to supply the user with the ability to test F prime flight software in an
41
- integrated configuration with ground in-the-loop.
42
-
@@ -1 +0,0 @@
1
- fprime_gds