├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── pyjs9 ├── __init__.py └── pylintrc └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | # scratch files 2 | *~ 3 | *.bak 4 | foo* 5 | 6 | # scratch diretories 7 | /test/ 8 | /gen/ 9 | 10 | # Compiled python modules. 11 | *.pyc 12 | 13 | # Setuptools distribution folder. 14 | /dist/ 15 | 16 | # Build directory 17 | /build/ 18 | 19 | # Python egg metadata, regenerated from source files by setuptools. 20 | /*.egg-info 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | JS9 is distributed under the terms of The MIT License (MIT), reproduced below. 2 | 3 | Copyright (c) 2014 Smithsonian Institution 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.rst 3 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | **My software career comes to an end on July 1, 2022 and with it will end active development of pyjs9. Please plan accordingly. Eric** 2 | 3 | .. image:: https://zenodo.org/badge/DOI/10.5281/zenodo.6675877.svg 4 | :target: https://doi.org/10.5281/zenodo.6675877 5 | 6 | JS9 brings image display right into your browser:: 7 | 8 | - display URL-based FITS images and binary tables 9 | - drag and drop FITS images and binary tables 10 | - change the colormap and scale 11 | - manipulate the contrast/bias with the mouse 12 | - display pixel values and WCS position information 13 | - create and manipulate geometric regions of interest 14 | - add your own extensions using plugins and the Public API 15 | - perform data analysis (local and server-side) 16 | - display RGB composite images 17 | - control JS9 from the command line 18 | - print images 19 | - much more ... 20 | 21 | See: https://js9.si.edu for more information about JS9. 22 | 23 | pyjs9.py connects Python and JS9 via the js9Helper.js back-end server:: 24 | 25 | - The JS9 class constructor connects to a single JS9 instance in a Web page. 26 | - The JS9 object supports the JS9 Public API and a shorter command-line syntax. 27 | - See: https://js9.si.edu/js9/help/publicapi.html for info about the public api 28 | - Send/retrieve numpy arrays and astropy (or pyfits) hdulists to/from JS9. 29 | 30 | Requirements: Python 3.5 or higher is required. Beyond that, pyjs9 31 | communicates with a JS9 back-end Node server (which communicates with 32 | the browser itself). By default, pyjs9 utilizes the `requests 33 | ` module to communicate 34 | with the JS9 back-end server. However, if you install 35 | `python-socketio` `, pyjs9 36 | will use the faster, persistent `socket.io http://socket.io/` 37 | protocol. 38 | 39 | Install from the repository using pip3, as usual:: 40 | 41 | > pip3 install git+https://github.com/ericmandel/pyjs9.git#egg=pyjs9 42 | 43 | or from a local copy:: 44 | 45 | > pip3 install /path/to/local/copy 46 | 47 | Mandatory dependencies:: 48 | 49 | requests 50 | 51 | Optional dependencies:: 52 | 53 | numpy # support for GetNumpy and SetNumpy methods 54 | astropy # support for GetFITS and SetFITS methods 55 | python-socketio # fast, persistent socket.io protocol, instead of html 56 | # (install version 5.x, version 4.x is deprecated) 57 | 58 | To run:: 59 | 60 | > # ensure JS9 node-server is running ... 61 | > # visit your local JS9 Web page in your browser ... 62 | > python 63 | ... (startup messages) ... 64 | >>> import pyjs9 65 | >>> 66 | >>> j = pyjs9.JS9() # default: connect to 'http://localhost' 67 | >>> 68 | >>> j.GetColormap() 69 | {'bias': 0.5, 'colormap': 'grey', 'contrast': 1} 70 | >>> j.SetColormap('red') 71 | >>> j.cmap() 72 | 'red 1 0.5' 73 | >>> 74 | >>> hdul = j.GetFITS() 75 | >>> hdul.info() 76 | Filename: (No file associated with this HDUList) 77 | No. Name Type Cards Dimensions Format 78 | 0 PRIMARY PrimaryHDU 6 (1024, 1024) int32 79 | >>> 80 | >>> narr = j.GetNumpy() 81 | >>> narr.shape 82 | (1024, 1024) 83 | 84 | If you have internet connectivity, visit the JS9 Web page at 85 | https://js9.si.edu with your browser and:: 86 | 87 | > python 88 | ... (startup messages) ... 89 | >>> import pyjs9 90 | >>> 91 | >>> j = pyjs9.JS9('https://js9.si.edu') # connect to JS9 Web site 92 | >>> 93 | >>> j.GetColormap() 94 | {'bias': 0.5, 'colormap': 'grey', 'contrast': 1} 95 | >>> 96 | >>> # etc ... 97 | -------------------------------------------------------------------------------- /pyjs9/pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # A comma-separated list of package or module names from where C extensions may 4 | # be loaded. Extensions are loading into the active Python interpreter and may 5 | # run arbitrary code 6 | extension-pkg-whitelist= 7 | 8 | # Add files or directories to the blacklist. They should be base names, not 9 | # paths. 10 | ignore=CVS 11 | 12 | # Add files or directories matching the regex patterns to the blacklist. The 13 | # regex matches against base names, not paths. 14 | ignore-patterns= 15 | 16 | # Python code to execute, usually for sys.path manipulation such as 17 | # pygtk.require(). 18 | #init-hook= 19 | 20 | # Use multiple processes to speed up Pylint. 21 | jobs=1 22 | 23 | # List of plugins (as comma separated values of python modules names) to load, 24 | # usually to register additional checkers. 25 | load-plugins= 26 | 27 | # Pickle collected data for later comparisons. 28 | persistent=yes 29 | 30 | # Specify a configuration file. 31 | #rcfile= 32 | 33 | # When enabled, pylint would attempt to guess common misconfiguration and emit 34 | # user-friendly hints instead of false-positive error messages 35 | suggestion-mode=yes 36 | 37 | # Allow loading of arbitrary C extensions. Extensions are imported into the 38 | # active Python interpreter and may run arbitrary code. 39 | unsafe-load-any-extension=no 40 | 41 | 42 | [MESSAGES CONTROL] 43 | 44 | # Only show warnings with the listed confidence levels. Leave empty to show 45 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED 46 | confidence= 47 | 48 | # Disable the message, report, category or checker with the given id(s). You 49 | # can either give multiple identifiers separated by comma (,) or put this 50 | # option multiple times (only on the command line, not in the configuration 51 | # file where it should appear only once).You can also use "--disable=all" to 52 | # disable everything first and then reenable specific checks. For example, if 53 | # you want to run only the similarities checker, you can use "--disable=all 54 | # --enable=similarities". If you want to run only the classes checker, but have 55 | # no Warning level messages displayed, use"--disable=all --enable=classes 56 | # --disable=W" 57 | # added by egm 58 | disable=print-statement, 59 | parameter-unpacking, 60 | unpacking-in-except, 61 | old-raise-syntax, 62 | backtick, 63 | long-suffix, 64 | old-ne-operator, 65 | old-octal-literal, 66 | import-star-module-level, 67 | non-ascii-bytes-literal, 68 | invalid-unicode-literal, 69 | raw-checker-failed, 70 | bad-inline-option, 71 | locally-disabled, 72 | locally-enabled, 73 | file-ignored, 74 | suppressed-message, 75 | useless-suppression, 76 | deprecated-pragma, 77 | apply-builtin, 78 | basestring-builtin, 79 | buffer-builtin, 80 | cmp-builtin, 81 | coerce-builtin, 82 | execfile-builtin, 83 | file-builtin, 84 | long-builtin, 85 | raw_input-builtin, 86 | reduce-builtin, 87 | standarderror-builtin, 88 | unicode-builtin, 89 | xrange-builtin, 90 | coerce-method, 91 | delslice-method, 92 | getslice-method, 93 | setslice-method, 94 | no-absolute-import, 95 | old-division, 96 | dict-iter-method, 97 | dict-view-method, 98 | next-method-called, 99 | metaclass-assignment, 100 | indexing-exception, 101 | raising-string, 102 | reload-builtin, 103 | oct-method, 104 | hex-method, 105 | nonzero-method, 106 | cmp-method, 107 | input-builtin, 108 | round-builtin, 109 | intern-builtin, 110 | unichr-builtin, 111 | map-builtin-not-iterating, 112 | zip-builtin-not-iterating, 113 | range-builtin-not-iterating, 114 | filter-builtin-not-iterating, 115 | using-cmp-argument, 116 | eq-without-hash, 117 | div-method, 118 | idiv-method, 119 | rdiv-method, 120 | exception-message-attribute, 121 | invalid-str-codec, 122 | sys-max-int, 123 | bad-python3-import, 124 | deprecated-string-function, 125 | deprecated-str-translate-call, 126 | deprecated-itertools-function, 127 | deprecated-types-field, 128 | next-method-defined, 129 | dict-items-not-iterating, 130 | dict-keys-not-iterating, 131 | dict-values-not-iterating, 132 | deprecated-operator-function, 133 | deprecated-urllib-function, 134 | xreadlines-attribute, 135 | deprecated-sys-function, 136 | exception-escape, 137 | comprehension-escape, 138 | fixme, 139 | invalid-name, 140 | too-many-public-methods, 141 | too-many-lines 142 | 143 | # Enable the message, report, category or checker with the given id(s). You can 144 | # either give multiple identifier separated by comma (,) or put this option 145 | # multiple time (only on the command line, not in the configuration file where 146 | # it should appear only once). See also the "--disable" option for examples. 147 | enable=c-extension-no-member 148 | 149 | 150 | [REPORTS] 151 | 152 | # Python expression which should return a note less than 10 (10 is the highest 153 | # note). You have access to the variables errors warning, statement which 154 | # respectively contain the number of errors / warnings messages and the total 155 | # number of statements analyzed. This is used by the global evaluation report 156 | # (RP0004). 157 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 158 | 159 | # Template used to display messages. This is a python new-style format string 160 | # used to format the message information. See doc for all details 161 | #msg-template= 162 | 163 | # Set the output format. Available formats are text, parseable, colorized, json 164 | # and msvs (visual studio).You can also give a reporter class, eg 165 | # mypackage.mymodule.MyReporterClass. 166 | output-format=text 167 | 168 | # Tells whether to display a full report or only the messages 169 | reports=no 170 | 171 | # Activate the evaluation score. 172 | score=yes 173 | 174 | 175 | [REFACTORING] 176 | 177 | # Maximum number of nested blocks for function / method body 178 | max-nested-blocks=5 179 | 180 | # Complete name of functions that never returns. When checking for 181 | # inconsistent-return-statements if a never returning function is called then 182 | # it will be considered as an explicit return statement and no message will be 183 | # printed. 184 | never-returning-functions=optparse.Values,sys.exit 185 | 186 | 187 | [LOGGING] 188 | 189 | # Logging modules to check that the string format arguments are in logging 190 | # function parameter format 191 | logging-modules=logging 192 | 193 | 194 | [SPELLING] 195 | 196 | # Limits count of emitted suggestions for spelling mistakes 197 | max-spelling-suggestions=4 198 | 199 | # Spelling dictionary name. Available dictionaries: none. To make it working 200 | # install python-enchant package. 201 | spelling-dict= 202 | 203 | # List of comma separated words that should not be checked. 204 | spelling-ignore-words= 205 | 206 | # A path to a file that contains private dictionary; one word per line. 207 | spelling-private-dict-file= 208 | 209 | # Tells whether to store unknown words to indicated private dictionary in 210 | # --spelling-private-dict-file option instead of raising a message. 211 | spelling-store-unknown-words=no 212 | 213 | 214 | [MISCELLANEOUS] 215 | 216 | # List of note tags to take in consideration, separated by a comma. 217 | notes=FIXME, 218 | XXX, 219 | TODO 220 | 221 | 222 | [SIMILARITIES] 223 | 224 | # Ignore comments when computing similarities. 225 | ignore-comments=yes 226 | 227 | # Ignore docstrings when computing similarities. 228 | ignore-docstrings=yes 229 | 230 | # Ignore imports when computing similarities. 231 | ignore-imports=no 232 | 233 | # Minimum lines number of a similarity. 234 | min-similarity-lines=4 235 | 236 | 237 | [TYPECHECK] 238 | 239 | # List of decorators that produce context managers, such as 240 | # contextlib.contextmanager. Add to this list to register other decorators that 241 | # produce valid context managers. 242 | contextmanager-decorators=contextlib.contextmanager 243 | 244 | # List of members which are set dynamically and missed by pylint inference 245 | # system, and so shouldn't trigger E1101 when accessed. Python regular 246 | # expressions are accepted. 247 | generated-members= 248 | 249 | # Tells whether missing members accessed in mixin class should be ignored. A 250 | # mixin class is detected if its name ends with "mixin" (case insensitive). 251 | ignore-mixin-members=yes 252 | 253 | # This flag controls whether pylint should warn about no-member and similar 254 | # checks whenever an opaque object is returned when inferring. The inference 255 | # can return multiple potential results while evaluating a Python object, but 256 | # some branches might not be evaluated, which results in partial inference. In 257 | # that case, it might be useful to still emit no-member and other checks for 258 | # the rest of the inferred objects. 259 | ignore-on-opaque-inference=yes 260 | 261 | # List of class names for which member attributes should not be checked (useful 262 | # for classes with dynamically set attributes). This supports the use of 263 | # qualified names. 264 | ignored-classes=optparse.Values,thread._local,_thread._local 265 | 266 | # List of module names for which member attributes should not be checked 267 | # (useful for modules/projects where namespaces are manipulated during runtime 268 | # and thus existing member attributes cannot be deduced by static analysis. It 269 | # supports qualified module names, as well as Unix pattern matching. 270 | ignored-modules= 271 | 272 | # Show a hint with possible names when a member name was not found. The aspect 273 | # of finding the hint is based on edit distance. 274 | missing-member-hint=yes 275 | 276 | # The minimum edit distance a name should have in order to be considered a 277 | # similar match for a missing member name. 278 | missing-member-hint-distance=1 279 | 280 | # The total number of similar names that should be taken in consideration when 281 | # showing a hint for a missing member. 282 | missing-member-max-choices=1 283 | 284 | 285 | [VARIABLES] 286 | 287 | # List of additional names supposed to be defined in builtins. Remember that 288 | # you should avoid to define new builtins when possible. 289 | additional-builtins= 290 | 291 | # Tells whether unused global variables should be treated as a violation. 292 | allow-global-unused-variables=yes 293 | 294 | # List of strings which can identify a callback function by name. A callback 295 | # name must start or end with one of those strings. 296 | callbacks=cb_, 297 | _cb 298 | 299 | # A regular expression matching the name of dummy variables (i.e. expectedly 300 | # not used). 301 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 302 | 303 | # Argument names that match this expression will be ignored. Default to name 304 | # with leading underscore 305 | ignored-argument-names=_.*|^ignored_|^unused_ 306 | 307 | # Tells whether we should check for unused import in __init__ files. 308 | init-import=no 309 | 310 | # List of qualified module names which can have objects that can redefine 311 | # builtins. 312 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,io,builtins 313 | 314 | 315 | [FORMAT] 316 | 317 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 318 | expected-line-ending-format= 319 | 320 | # Regexp for a line that is allowed to be longer than the limit. 321 | ignore-long-lines=^\s*(# )??$ 322 | 323 | # Number of spaces of indent required inside a hanging or continued line. 324 | indent-after-paren=4 325 | 326 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 327 | # tab). 328 | indent-string=' ' 329 | 330 | # Maximum number of characters on a single line. 331 | max-line-length=100 332 | 333 | # Maximum number of lines in a module 334 | max-module-lines=1000 335 | 336 | # List of optional constructs for which whitespace checking is disabled. `dict- 337 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 338 | # `trailing-comma` allows a space between comma and closing bracket: (a, ). 339 | # `empty-line` allows space-only lines. 340 | no-space-check=trailing-comma, 341 | dict-separator 342 | 343 | # Allow the body of a class to be on the same line as the declaration if body 344 | # contains single statement. 345 | single-line-class-stmt=no 346 | 347 | # Allow the body of an if to be on the same line as the test if there is no 348 | # else. 349 | single-line-if-stmt=no 350 | 351 | 352 | [BASIC] 353 | 354 | # Naming style matching correct argument names 355 | argument-naming-style=snake_case 356 | 357 | # Regular expression matching correct argument names. Overrides argument- 358 | # naming-style 359 | #argument-rgx= 360 | 361 | # Naming style matching correct attribute names 362 | attr-naming-style=snake_case 363 | 364 | # Regular expression matching correct attribute names. Overrides attr-naming- 365 | # style 366 | #attr-rgx= 367 | 368 | # Bad variable names which should always be refused, separated by a comma 369 | bad-names=foo, 370 | bar, 371 | baz, 372 | toto, 373 | tutu, 374 | tata 375 | 376 | # Naming style matching correct class attribute names 377 | class-attribute-naming-style=any 378 | 379 | # Regular expression matching correct class attribute names. Overrides class- 380 | # attribute-naming-style 381 | #class-attribute-rgx= 382 | 383 | # Naming style matching correct class names 384 | class-naming-style=PascalCase 385 | 386 | # Regular expression matching correct class names. Overrides class-naming-style 387 | #class-rgx= 388 | 389 | # Naming style matching correct constant names 390 | const-naming-style=UPPER_CASE 391 | 392 | # Regular expression matching correct constant names. Overrides const-naming- 393 | # style 394 | #const-rgx= 395 | 396 | # Minimum line length for functions/classes that require docstrings, shorter 397 | # ones are exempt. 398 | docstring-min-length=-1 399 | 400 | # Naming style matching correct function names 401 | function-naming-style=snake_case 402 | 403 | # Regular expression matching correct function names. Overrides function- 404 | # naming-style 405 | #function-rgx= 406 | 407 | # Good variable names which should always be accepted, separated by a comma 408 | good-names=i, 409 | j, 410 | k, 411 | ex, 412 | Run, 413 | _ 414 | 415 | # Include a hint for the correct naming format with invalid-name 416 | include-naming-hint=no 417 | 418 | # Naming style matching correct inline iteration names 419 | inlinevar-naming-style=any 420 | 421 | # Regular expression matching correct inline iteration names. Overrides 422 | # inlinevar-naming-style 423 | #inlinevar-rgx= 424 | 425 | # Naming style matching correct method names 426 | method-naming-style=snake_case 427 | 428 | # Regular expression matching correct method names. Overrides method-naming- 429 | # style 430 | #method-rgx= 431 | 432 | # Naming style matching correct module names 433 | module-naming-style=snake_case 434 | 435 | # Regular expression matching correct module names. Overrides module-naming- 436 | # style 437 | #module-rgx= 438 | 439 | # Colon-delimited sets of names that determine each other's naming style when 440 | # the name regexes allow several styles. 441 | name-group= 442 | 443 | # Regular expression which should only match function or class names that do 444 | # not require a docstring. 445 | no-docstring-rgx=^_ 446 | 447 | # List of decorators that produce properties, such as abc.abstractproperty. Add 448 | # to this list to register other decorators that produce valid properties. 449 | property-classes=abc.abstractproperty 450 | 451 | # Naming style matching correct variable names 452 | variable-naming-style=snake_case 453 | 454 | # Regular expression matching correct variable names. Overrides variable- 455 | # naming-style 456 | #variable-rgx= 457 | 458 | 459 | [DESIGN] 460 | 461 | # Maximum number of arguments for function / method 462 | max-args=5 463 | 464 | # Maximum number of attributes for a class (see R0902). 465 | max-attributes=7 466 | 467 | # Maximum number of boolean expressions in a if statement 468 | max-bool-expr=5 469 | 470 | # Maximum number of branch for function / method body 471 | max-branches=12 472 | 473 | # Maximum number of locals for function / method body 474 | max-locals=15 475 | 476 | # Maximum number of parents for a class (see R0901). 477 | max-parents=7 478 | 479 | # Maximum number of public methods for a class (see R0904). 480 | max-public-methods=20 481 | 482 | # Maximum number of return / yield for function / method body 483 | max-returns=6 484 | 485 | # Maximum number of statements in function / method body 486 | max-statements=50 487 | 488 | # Minimum number of public methods for a class (see R0903). 489 | min-public-methods=2 490 | 491 | 492 | [CLASSES] 493 | 494 | # List of method names used to declare (i.e. assign) instance attributes. 495 | defining-attr-methods=__init__, 496 | __new__, 497 | setUp 498 | 499 | # List of member names, which should be excluded from the protected access 500 | # warning. 501 | exclude-protected=_asdict, 502 | _fields, 503 | _replace, 504 | _source, 505 | _make 506 | 507 | # List of valid names for the first argument in a class method. 508 | valid-classmethod-first-arg=cls 509 | 510 | # List of valid names for the first argument in a metaclass class method. 511 | valid-metaclass-classmethod-first-arg=mcs 512 | 513 | 514 | [IMPORTS] 515 | 516 | # Allow wildcard imports from modules that define __all__. 517 | allow-wildcard-with-all=no 518 | 519 | # Analyse import fallback blocks. This can be used to support both Python 2 and 520 | # 3 compatible code, which means that the block might have code that exists 521 | # only in one or another interpreter, leading to false positives when analysed. 522 | analyse-fallback-blocks=no 523 | 524 | # Deprecated modules which should not be used, separated by a comma 525 | deprecated-modules=regsub, 526 | TERMIOS, 527 | Bastion, 528 | rexec 529 | 530 | # Create a graph of external dependencies in the given file (report RP0402 must 531 | # not be disabled) 532 | ext-import-graph= 533 | 534 | # Create a graph of every (i.e. internal and external) dependencies in the 535 | # given file (report RP0402 must not be disabled) 536 | import-graph= 537 | 538 | # Create a graph of internal dependencies in the given file (report RP0402 must 539 | # not be disabled) 540 | int-import-graph= 541 | 542 | # Force import order to recognize a module as part of the standard 543 | # compatibility libraries. 544 | known-standard-library= 545 | 546 | # Force import order to recognize a module as part of a third party library. 547 | known-third-party=enchant 548 | 549 | 550 | [EXCEPTIONS] 551 | 552 | # Exceptions that will emit a warning when being caught. Defaults to 553 | # "Exception" 554 | overgeneral-exceptions=Exception 555 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | def readme(): 4 | with open('README.rst') as f: 5 | return f.read() 6 | 7 | setup(name='pyjs9', 8 | version='3.8', 9 | description='Python/JS9 connection, with numpy and astropy/fits support', 10 | long_description=readme(), 11 | author='Eric Mandel', 12 | author_email='saord@cfa.harvard.edu', 13 | classifiers=[ 14 | 'Development Status :: 5 - Production/Stable', 15 | 'License :: OSI Approved :: MIT License', 16 | 'Programming Language :: Python :: 3', 17 | 'Topic :: Scientific/Engineering :: Astronomy', 18 | ], 19 | keywords='astronomy astrophysics image display', 20 | url='https://js9.si.edu', 21 | license='MIT', 22 | packages=['pyjs9'], 23 | install_requires=['requests'], 24 | extras_require={'all': ['numpy', 'astropy']}, 25 | zip_safe=False) 26 | --------------------------------------------------------------------------------