├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── codeql-analysis.yml │ ├── publish-to-test-pypi.yml │ └── python-publish.yml ├── .gitignore ├── ContributorAgreement.txt ├── LICENSE.txt ├── Readme.md ├── __init__.py ├── binder ├── environment.yml ├── postBuild └── sascfg_personal.py ├── iris.sas ├── notebook ├── LimitLogSize.ipynb ├── SAS Magic.ipynb ├── SAS SQL Example.ipynb ├── SASkernelLogMagic.ipynb ├── enhancedCellMagics.ipynb └── loadSASExtensions.ipynb ├── sas_kernel ├── __init__.py ├── __main__.py ├── data │ ├── __init__.py │ ├── logo-64x64.png │ ├── sasgrammardictionary.json │ ├── sasgrammerdictionary.json │ └── sasproclist.json ├── doc │ ├── Makefile │ ├── __init__.py │ ├── make.bat │ └── source │ │ ├── __init__.py │ │ ├── api.rst │ │ ├── conf.py │ │ ├── getting-started.rst │ │ ├── images │ │ ├── ap1.PNG │ │ ├── ap2.PNG │ │ ├── ap3.PNG │ │ ├── ap4.PNG │ │ ├── ap5.PNG │ │ ├── forest_loss_reduction.png │ │ ├── glm_rsq_ss1.png │ │ ├── heatmap_satisfaction_evaluation.png │ │ ├── hist_satisfaction_level.png │ │ ├── hpsplit_variable_importance.png │ │ ├── neural_fit_misclass.png │ │ ├── panel_left_sales.png │ │ └── sgplot_vbar_salary.png │ │ ├── index.rst │ │ ├── install.rst │ │ ├── license.rst │ │ └── overview.rst ├── install.py ├── kernel.py ├── magics │ ├── README.md │ ├── __init__.py │ ├── log_magic.py │ ├── prompt4var_magic.py │ └── sas_session_magic.py ├── nbextensions │ └── README.md ├── sasKernel.png ├── showSASLog │ ├── __init__.py │ ├── main.js │ ├── readme.md │ └── showSASLog.yaml ├── theme │ ├── __init__.py │ ├── base16-sas-light.css │ ├── readme.md │ ├── theme_selector.js │ └── theme_selector.yaml └── version.py ├── setup.cfg └── setup.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Enviornment (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Python Version [e.g. 3.7] 30 | - SAS Version [e.g. 9.4M7, Viya 3.5] 31 | - Connection Method [e.g. IOM, STDIO, HTTP, SSH, etc.] 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '20 7 * * 5' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript', 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v2 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-test-pypi.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Publish Package to TestPyPI 5 | 6 | on: push 7 | 8 | jobs: 9 | deploy: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Set up Python 16 | uses: actions/setup-python@v2 17 | with: 18 | python-version: '3.x' 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip 22 | pip install setuptools wheel twine 23 | - name: Build and publish 24 | env: 25 | TWINE_USERNAME: __token__ 26 | TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }} 27 | run: | 28 | python setup.py sdist bdist_wheel 29 | twine upload --repository testpypi dist/* 30 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Publish Package to PyPI 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | workflow_dispatch: 11 | 12 | jobs: 13 | deploy: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: '3.x' 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install setuptools wheel twine 27 | - name: Build and publish 28 | env: 29 | TWINE_USERNAME: __token__ 30 | TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 31 | run: | 32 | # python setup.py sdist bdist_wheel 33 | python setup.py sdist 34 | twine upload dist/* 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.pyc 3 | build/ 4 | dist/ 5 | MANIFEST 6 | **/*checkpoint.ipynb 7 | **/*.ipynb 8 | **/*.sas7bcat 9 | sascfg_personal.py 10 | **/*.ipynb_checkpoints/* 11 | *.egg-info/** 12 | .eggs/** 13 | -------------------------------------------------------------------------------- /ContributorAgreement.txt: -------------------------------------------------------------------------------- 1 | Contributor Agreement 2 | 3 | Version 1.1 4 | 5 | Contributions to this software are accepted only when they are 6 | properly accompanied by a Contributor Agreement. The Contributor 7 | Agreement for this software is the Developer's Certificate of Origin 8 | 1.1 (DCO) as provided with and required for accepting contributions 9 | to the Linux kernel. 10 | 11 | In each contribution proposed to be included in this software, the 12 | developer must include a "sign-off" that denotes consent to the 13 | terms of the Developer's Certificate of Origin. The sign-off is 14 | a line of text in the description that accompanies the change, 15 | certifying that you have the right to provide the contribution 16 | to be included. For changes provided in source code control (for 17 | example, via a Git pull request) the sign-off must be included in 18 | the commit message in source code control. For changes provided 19 | in email or issue tracking, the sign-off must be included in the 20 | email or the issue, and the sign-off will be incorporated into the 21 | permanent commit message if the contribution is accepted into the 22 | official source code. 23 | 24 | If you can certify the below: 25 | 26 | Developer's Certificate of Origin 1.1 27 | 28 | By making a contribution to this project, I certify that: 29 | 30 | (a) The contribution was created in whole or in part by me and I 31 | have the right to submit it under the open source license 32 | indicated in the file; or 33 | 34 | (b) The contribution is based upon previous work that, to the best 35 | of my knowledge, is covered under an appropriate open source 36 | license and I have the right under that license to submit that 37 | work with modifications, whether created in whole or in part 38 | by me, under the same open source license (unless I am 39 | permitted to submit under a different license), as indicated 40 | in the file; or 41 | 42 | (c) The contribution was provided directly to me by some other 43 | person who certified (a), (b) or (c) and I have not modified 44 | it. 45 | 46 | (d) I understand and agree that this project and the contribution 47 | are public and that a record of the contribution (including all 48 | personal information I submit with it, including my sign-off) is 49 | maintained indefinitely and may be redistributed consistent with 50 | this project or the open source license(s) involved. 51 | 52 | then you just add a line saying 53 | 54 | Signed-off-by: Random J Developer 55 | 56 | using your real name (sorry, no pseudonyms or anonymous contributions.) 57 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # SAS Kernel for Jupyter 2 | 3 | 4 | 5 | [![Publish Python Package](https://github.com/sassoftware/sas_kernel/actions/workflows/python-publish.yml/badge.svg)](https://github.com/sassoftware/sas_kernel/actions/workflows/python-publish.yml) 6 | ## Overview 7 | 8 | The SAS Kernel for [Jupyter Notebooks](http://www.jupyter.org) is capable of running SAS programs from within the Jupyter interface. 9 | The SAS kernel allows a user to leverage all of the SAS products they have licensed. 10 | 11 | After installing the SAS kernel, you can use a notebook and a SAS installation to write, document, and submit SAS programming statements. The Jupyter notebook interface allows sharing of results through JSON and the SAS kernel is no exception, you can share code and results in a static form through the Jupyter notebook. 12 | 13 | ## Documentation 14 | 15 | Here is the link to the current documentation 16 | 17 | ## Prerequisites 18 | 19 | - Python3 (this is now the default since Python2 went end of life in January 2020) 20 | - Jupyter version 4 or higher 21 | - SAS 9.4 or higher -- This includes [SAS Viya](http://www.sas.com/en_us/software/viya.html). The SAS kernel is compatible with any version of SAS released since July 2013. 22 | - SASPy -- The SAS kernel has a dependency on [SASPy](https://github.com/sassoftware/saspy). The package will be installed automatically but it must be configured to access your available SAS server. **SASPy must be [configured](https://sassoftware.github.io/saspy/install.html#configuration) before the SAS kernel can work successfully**. 23 | 24 | ## Installation 25 | 26 | This will install the SAS Kernel for jupyter as well as the Jupyter lab extensions (jupyterlab v3+ is required) to make you a more productive programmer within Jupyter. [Here are details](https://github.com/jld23/sas_kernel_ext) about the extensions. 27 | 28 | ```bash 29 | pip install SAS-kernel['jlab_ext'] 30 | ``` 31 | 32 | ### The common methods to install are 33 | 34 | 1. `pip` -- PIP is the most common way to install the latest stable version of the code. 35 | 36 | ```bash 37 | pip install sas_kernel 38 | ``` 39 | 40 | 1. `conda` -- A conda package is also available if you prefer to use conda as your package manger 41 | 42 | ```bash 43 | conda install -c anaconda sas_kernel 44 | ``` 45 | 46 | 1. From source -- If you need to install from the source branch before a new version has been built and pushed you can install from source like this: 47 | 48 | ```bash 49 | pip install git+https://git@github.com/sassoftware/sas_kernel.git@main 50 | ``` 51 | 52 | Note that the default branch is now `main` to match the GitHub convention. You can modify the about URL if you're installing from a fork or a non-default branch. 53 | 54 | ### To verify that the sas_kernel is installed 55 | 56 | ```bash 57 | jupyter kernelspec list 58 | ``` 59 | 60 | You should see output _similar_ to code below: 61 | 62 | ```bash 63 | Available kernels: 64 | python3 /home/sas/anaconda3/lib/python3.5/site-packages/ipykernel/resources 65 | sas /home/sas/.local/share/jupyter/kernels/sas 66 | ``` 67 | 68 | **NOTE:** You will not be able to execute SAS code through Jupyter until you have [configured SASPy](https://sassoftware.github.io/saspy/install.html#configuration). 69 | 70 | ## Getting Started 71 | 72 | Here is a basic example of programming with SAS and Jupyter Notebook: [Getting Started](https://sassoftware.github.io/sas_kernel/getting-started.html) 73 | 74 | ### Improving Usability 75 | 76 | #### For the Jupyter Lab extensions 77 | 78 | There is a seperate repository where the extensions are developed and maintained. See [that repo](https://github.com/jld23/sas_kernel_ext) for details 79 | 80 | #### For the Legacy Jupyter Notebook 81 | 82 | There are a few NBExtensions that have been created to make working with Jupyter notebooks more productive. These are largely the result of pain points from my use of SAS Kernel for programming tasks. The extensions can be found [here](./sas_kernel/nbextensions). The list includes: 83 | 84 | - SAS Log -- which show the SAS log for the last executed cell or the entire log since the last (re)start of the notebook 85 | - themes -- this allows you to change the color scheme for your code to match the traditional SAS color scheme 86 | 87 | **NOTE:** These extensions are for Jupyter _Notebook_ they are not compatable with Jupyter _Lab_. Jupyter Lab extensions are in development and will be released shortly. 88 | 89 | #### Installing the SAS Extensions 90 | 91 | Details for installing the extensions for SAS can be found [here](./sas_kernel/nbextensions/README.md). 92 | 93 | #### Jupyter Magics for the sas_kernel 94 | 95 | There are magics that have been written specifically for the sas_kernel to get more details see the [README](./sas_kernel/magics/README.md). 96 | 97 | ### NBGrader 98 | 99 | [nbgrader](http://nbgrader.readthedocs.org/en/stable/) is a system for assigning and grading notebooks and extends jupyter. NBgrader is compatible with the SAS kernel. The work was merged in [September 2020](https://github.com/jupyter/nbgrader/pull/1356). It will be widely available with the next release of NBGrader (0.62), until then you can install from source. 100 | 101 | ## FAQ 102 | 103 | - Is there a SAS Magic that I can access from a python kernel? 104 | 105 | Yes! There are actually several cell magics available from SAS. 106 | They are `%%SAS`, `%%IML`, and `%%OPTMODEL`. To load these magics in your notebook, execute the following command `%load_ext saspy.sas_magic`. You can check that the magics have are successfully activated by looking at the results of `%lsmagic` and looking in the cell magic section. 107 | If you use multiple SAS Cell magics in the _same_ notebook they will share a SAS session (have the same WORK libname and MACROS). There is currently no sharing of SAS Sessions between different notebooks. 108 | 109 | - Do I need to buy SAS to use this kernel? 110 | 111 | The SAS Kernel is simply a gateway for Jupyter notebooks to talk to SAS, as such, if SAS is not installed this kernel won't be helpful. For information on purchasing SAS [click here](http://www.sas.com/en_us/software/how-to-buy.html). 112 | 113 | - How does Jupyter communicate with SAS? 114 | 115 | Behind a Jupyter notebook is a python session, that python session submits code to SAS and receives responses through various pathways (depending on the SASPy configuration). Jupyter can communicate with any SAS host (Windows, Linux, Unix, MVS) that has been released since July 2013 to present. 116 | 117 | - How can I see my SAS log, I only see the listing output? 118 | 119 | SAS is different from many other programming languages in that it has two useful information streams, the log (which details the technical details of what happened and how long it took) and the lst (which includes the tables and graphics from the analysis). The SAS Kernel attempts to show you what I _think_ you want. Here are the rules: 120 | 121 | | LOG | LST | DISPLAYED | NOTES | 122 | | --------------------------- | --- | ----------------------------------------------------------------- | ----------------------------------------------------------------------- | 123 | | Yes | No | LOG | This happens when you run DATA Step or a PROC with the `noprint` option | 124 | | Yes | Yes | LST | --- | 125 | | Yes (with ERROR message(s)) | Yes | ERROR messages with context from the log, then the listing output | --- | 126 | | Yes (with ERROR message(s)) | No | LOG | --- | 127 | 128 | If you want to see the log but it was not displayed you can use [SASLog NBExtension](./sas_kernel/nbextensions/README.md) which will show the log for the last executed cell or the entire log since the last (re)start of the notebook. 129 | 130 | - Will this leave a bunch of SAS sessions hanging around? 131 | 132 | A SAS session is started for each notebook you have open i.e. 5 notebooks open = 5 SAS sessions. Those sessions will remain active for the life of the notebook. If you shutdown your notebook, the SAS session will also terminate. In JupyterHub, there are configuration options to shutdown inactive sessions and the SAS kernel complies with those directives. 133 | 134 | - I restarted my SAS Kernel and now my WORK library is now empty. What happened? 135 | 136 | When you restart the kernel in a notebook you are terminating the current SAS session and starting a new one. All of the temporary artifacts, data sets in the WORK library, assigned libnames, filename, WORK macros, and so on are destroyed. 137 | 138 | ## Contributing 139 | 140 | The [Contributor Agreement](https://github.com/sassoftware/sas_kernel/blob/master/ContributorAgreement.txt) details how contributions can be made. 141 | 142 | ## Licensing 143 | 144 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at [LICENSE.txt](https://github.com/sassoftware/sas_kernel/blob/master/LICENSE.txt) 145 | 146 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. 147 | 148 | 149 | Add new section for github actions 150 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright SAS Institute 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the License); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | def isnotebook(): 18 | try: 19 | shell = get_ipython().__class__.__name__ 20 | if shell == 'ZMQInteractiveShell': 21 | return True # Jupyter notebook or qtconsole 22 | elif shell == 'TerminalInteractiveShell': 23 | return False # Terminal running IPython 24 | else: 25 | return False # Other type (?) 26 | except NameError: 27 | return False # Probably standard Python interpreter 28 | 29 | if isnotebook(): 30 | from sas_kernel.magics.log_magic import logMagic 31 | get_ipython().register_magics(logMagic) 32 | -------------------------------------------------------------------------------- /binder/environment.yml: -------------------------------------------------------------------------------- 1 | name: sas-kernel 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python 6 | - pip 7 | - jupyterlab=3 8 | - pip: 9 | - saspy 10 | - pandas 11 | - sas_kernel 12 | - jlab-create-sas-file 13 | - sas2nb 14 | - sas_log_viewer >= 0.2 15 | 16 | -------------------------------------------------------------------------------- /binder/postBuild: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Copyright SAS Institute 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the License); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | # copy sascfg_personal.py to default location 20 | mkdir -p ~/.config/saspy/ 21 | cp ./binder/sascfg_personal.py ~/.config/saspy/ 22 | 23 | #set -ex 24 | jupyter kernelspec list 25 | jupyter nbextension list 26 | 27 | 28 | jupyter nbextension install --py sas_kernel.showSASLog --user 29 | jupyter nbextension enable sas_kernel.showSASLog --py 30 | 31 | jupyter nbextension install --py sas_kernel.theme --user 32 | jupyter nbextension enable sas_kernel.theme --py 33 | 34 | # jupyter nbextension list 35 | 36 | ## build sas2nb from source 37 | # git clone https://github.com/jld23/sas_kernel_ext.git 38 | # cd sas_kernel_ext/sas2nb 39 | 40 | # pip install -e . 41 | # # Link your development version of the extension with JupyterLab 42 | # jupyter labextension develop . --overwrite 43 | # # Rebuild extension Typescript source after making changes 44 | # jlpm run build 45 | 46 | 47 | -------------------------------------------------------------------------------- /binder/sascfg_personal.py: -------------------------------------------------------------------------------- 1 | SAS_config_names = ['viya4'] 2 | 3 | viya4 = { 4 | 'url':'https://binder.demo.sas.com', 5 | 'verify': False, 6 | 'context':'SAS Job Execution compute context', 7 | 'client_id' : "SASPy", 8 | 'client_secret' : "7c301751-3114-4554-ad82-e20e3466bb17", 9 | 10 | } 11 | -------------------------------------------------------------------------------- /iris.sas: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------- 2 | S A S S A M P L E L I B R A R Y 3 | 4 | NAME: FASTEX1 5 | TITLE: Example 1 for PROC FASTCLUS 6 | PRODUCT: STAT 7 | SYSTEM: ALL 8 | KEYS: cluster analysis, Fisher's Iris Data 9 | PROCS: FASTCLUS, FREQ, CANDISC, SGPLOT 10 | DATA: 11 | 12 | SUPPORT: sasrbk 13 | REF: PROC FASTCLUS, EXAMPLE 1. 14 | MISC: 15 | -----------------------------------------------------------------*/ 16 | 17 | proc format; 18 | value specname 19 | 1='Setosa ' 20 | 2='Versicolor' 21 | 3='Virginica '; 22 | run; 23 | 24 | data iris; 25 | title 'Fisher (1936) Iris Data'; 26 | input SepalLength SepalWidth PetalLength PetalWidth Species @@; 27 | format Species specname.; 28 | label SepalLength='Sepal Length in mm.' 29 | SepalWidth ='Sepal Width in mm.' 30 | PetalLength='Petal Length in mm.' 31 | PetalWidth ='Petal Width in mm.'; 32 | datalines; 33 | 50 33 14 02 1 64 28 56 22 3 65 28 46 15 2 67 31 56 24 3 34 | 63 28 51 15 3 46 34 14 03 1 69 31 51 23 3 62 22 45 15 2 35 | 59 32 48 18 2 46 36 10 02 1 61 30 46 14 2 60 27 51 16 2 36 | 65 30 52 20 3 56 25 39 11 2 65 30 55 18 3 58 27 51 19 3 37 | 68 32 59 23 3 51 33 17 05 1 57 28 45 13 2 62 34 54 23 3 38 | 77 38 67 22 3 63 33 47 16 2 67 33 57 25 3 76 30 66 21 3 39 | 49 25 45 17 3 55 35 13 02 1 67 30 52 23 3 70 32 47 14 2 40 | 64 32 45 15 2 61 28 40 13 2 48 31 16 02 1 59 30 51 18 3 41 | 55 24 38 11 2 63 25 50 19 3 64 32 53 23 3 52 34 14 02 1 42 | 49 36 14 01 1 54 30 45 15 2 79 38 64 20 3 44 32 13 02 1 43 | 67 33 57 21 3 50 35 16 06 1 58 26 40 12 2 44 30 13 02 1 44 | 77 28 67 20 3 63 27 49 18 3 47 32 16 02 1 55 26 44 12 2 45 | 50 23 33 10 2 72 32 60 18 3 48 30 14 03 1 51 38 16 02 1 46 | 61 30 49 18 3 48 34 19 02 1 50 30 16 02 1 50 32 12 02 1 47 | 61 26 56 14 3 64 28 56 21 3 43 30 11 01 1 58 40 12 02 1 48 | 51 38 19 04 1 67 31 44 14 2 62 28 48 18 3 49 30 14 02 1 49 | 51 35 14 02 1 56 30 45 15 2 58 27 41 10 2 50 34 16 04 1 50 | 46 32 14 02 1 60 29 45 15 2 57 26 35 10 2 57 44 15 04 1 51 | 50 36 14 02 1 77 30 61 23 3 63 34 56 24 3 58 27 51 19 3 52 | 57 29 42 13 2 72 30 58 16 3 54 34 15 04 1 52 41 15 01 1 53 | 71 30 59 21 3 64 31 55 18 3 60 30 48 18 3 63 29 56 18 3 54 | 49 24 33 10 2 56 27 42 13 2 57 30 42 12 2 55 42 14 02 1 55 | 49 31 15 02 1 77 26 69 23 3 60 22 50 15 3 54 39 17 04 1 56 | 66 29 46 13 2 52 27 39 14 2 60 34 45 16 2 50 34 15 02 1 57 | 44 29 14 02 1 50 20 35 10 2 55 24 37 10 2 58 27 39 12 2 58 | 47 32 13 02 1 46 31 15 02 1 69 32 57 23 3 62 29 43 13 2 59 | 74 28 61 19 3 59 30 42 15 2 51 34 15 02 1 50 35 13 03 1 60 | 56 28 49 20 3 60 22 40 10 2 73 29 63 18 3 67 25 58 18 3 61 | 49 31 15 01 1 67 31 47 15 2 63 23 44 13 2 54 37 15 02 1 62 | 56 30 41 13 2 63 25 49 15 2 61 28 47 12 2 64 29 43 13 2 63 | 51 25 30 11 2 57 28 41 13 2 65 30 58 22 3 69 31 54 21 3 64 | 54 39 13 04 1 51 35 14 03 1 72 36 61 25 3 65 32 51 20 3 65 | 61 29 47 14 2 56 29 36 13 2 69 31 49 15 2 64 27 53 19 3 66 | 68 30 55 21 3 55 25 40 13 2 48 34 16 02 1 48 30 14 01 1 67 | 45 23 13 03 1 57 25 50 20 3 57 38 17 03 1 51 38 15 03 1 68 | 55 23 40 13 2 66 30 44 14 2 68 28 48 14 2 54 34 17 02 1 69 | 51 37 15 04 1 52 35 15 02 1 58 28 51 24 3 67 30 50 17 2 70 | 63 33 60 25 3 53 37 15 02 1 71 | ; 72 | 73 | proc fastclus data=iris maxc=2 maxiter=10 out=clus; 74 | var SepalLength SepalWidth PetalLength PetalWidth; 75 | run; 76 | 77 | proc freq; 78 | tables cluster*species; 79 | run; 80 | 81 | proc fastclus data=iris maxc=3 maxiter=10 out=clus; 82 | var SepalLength SepalWidth PetalLength PetalWidth; 83 | run; 84 | 85 | proc freq; 86 | tables cluster*Species; 87 | run; 88 | 89 | proc candisc anova out=can; 90 | class cluster; 91 | var SepalLength SepalWidth PetalLength PetalWidth; 92 | title2 'Canonical Discriminant Analysis of Iris Clusters'; 93 | run; 94 | 95 | proc sgplot data=Can; 96 | scatter y=Can2 x=Can1 / group=Cluster; 97 | title2 'Plot of Canonical Variables Identified by Cluster'; 98 | run; 99 | -------------------------------------------------------------------------------- /notebook/LimitLogSize.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 6, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [ 10 | { 11 | "data": { 12 | "text/html": [ 13 | "\n", 15 | "\n", 16 | "\n", 17 | "\n", 18 | " SAS Log\n", 19 | " \n", 20 | " \n", 73 | "\n", 74 | "\n", 75 | "

SAS Log

\n", 76 | "\n", 77 | "
128  ods listing close;ods html5 file=stdout options(bitmap_mode='inline') device=png; ods graphics on / outputfmt=png;
NOTE: Writing HTML5 Body file: STDOUT
129 ;*';*";*/;
130 proc import
131 datafile='adult.data'
132 dbms=csv out=df replace;
133 getnames=no;
134 run;
134! ;*';*";*/;
135 /**********************************************************************
136 * PRODUCT: SAS
137 * VERSION: 9.4
138 * CREATOR: External File Interface
139 * DATE: 18FEB16
140 * DESC: Generated SAS Datastep Code
141 * TEMPLATE SOURCE: (None Specified.)
142 ***********************************************************************/
143 data WORK.DF ;
144 %let _EFIERR_ = 0; /* set the ERROR detection macro variable */
145 infile 'adult.data' delimiter = ',' MISSOVER DSD lrecl=32767 ;
146 informat VAR1 best32. ;
147 informat VAR2 $16. ;
148 informat VAR3 best32. ;
149 informat VAR4 $12. ;
150 informat VAR5 best32. ;
151 informat VAR6 $21. ;
152 informat VAR7 $17. ;
153 informat VAR8 $13. ;
154 informat VAR9 $18. ;
155 informat VAR10 $6. ;
156 informat VAR11 best32. ;
157 informat VAR12 best32. ;
158 informat VAR13 best32. ;
159 informat VAR14 $13. ;
160 informat VAR15 $5. ;
161 format VAR1 best12. ;
162 format VAR2 $16. ;
163 format VAR3 best12. ;
164 format VAR4 $12. ;
165 format VAR5 best12. ;
166 format VAR6 $21. ;
167 format VAR7 $17. ;
168 format VAR8 $13. ;
169 format VAR9 $18. ;
170 format VAR10 $6. ;
171 format VAR11 best12. ;
172 format VAR12 best12. ;
173 format VAR13 best12. ;
174 format VAR14 $13. ;
175 format VAR15 $5. ;
176 input
177 VAR1
178 VAR2 $
179 VAR3
180 VAR4 $
181 VAR5
182 VAR6 $
183 VAR7 $
184 VAR8 $
185 VAR9 $
186 VAR10 $
187 VAR11
188 VAR12
189 VAR13
190 VAR14 $
191 VAR15 $
192 ;
193 if _ERROR_ then call symputx('_EFIERR_',1); /* set ERROR detection macro variable */
194 run;
NOTE: The infile 'adult.data' is:
Filename=/opt/notebook/adult.data,
Owner Name=root,Group Name=root,
Access Permission=-rw-r--r--,
Last Modified=16Feb2016:15:18:19,
File Size (bytes)=3974305

NOTE: 32562 records were read from the infile 'adult.data'.
The minimum record length was 0.
The maximum record length was 153.
NOTE: The data set WORK.DF has 32562 observations and 15 variables.
NOTE: DATA statement used (Total process time):
real time 0.04 seconds
cpu time 0.00 seconds

32562 rows created in WORK.DF from adult.data.



NOTE: WORK.DF data set was successfully created.
NOTE: The data set WORK.DF has 32562 observations and 15 variables.
NOTE: PROCEDURE IMPORT used (Total process time):
real time 0.08 seconds
cpu time 0.09 seconds

195 ods html5 close;ods listing;

196
\n", 78 | "\n", 79 | "\n" 80 | ], 81 | "text/plain": [ 82 | "" 83 | ] 84 | }, 85 | "execution_count": 6, 86 | "metadata": {}, 87 | "output_type": "execute_result" 88 | } 89 | ], 90 | "source": [ 91 | "proc import \n", 92 | " datafile='adult.data'\n", 93 | " dbms=csv out=df replace;\n", 94 | " getnames=no;\n", 95 | "run;" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 1, 101 | "metadata": { 102 | "collapsed": false 103 | }, 104 | "outputs": [ 105 | { 106 | "data": { 107 | "text/html": [ 108 | "\n", 110 | "\n", 111 | "\n", 112 | "\n", 113 | " \n", 114 | " \n", 115 | " \n", 168 | "\n", 169 | "\n", 170 | "

\n", 171 | "\n", 172 | "
11   ods listing close;ods html5 file=stdout options(bitmap_mode='inline') device=png; ods graphics on / outputfmt=png;
NOTE: Writing HTML5 Body file: STDOUT
12
13 options nosource nonotes;
32562 rows created in WORK.DF from adult.data.



80 ods html5 close;ods listing;

81
\n", 173 | "\n", 174 | "\n" 175 | ], 176 | "text/plain": [ 177 | "" 178 | ] 179 | }, 180 | "execution_count": 1, 181 | "metadata": {}, 182 | "output_type": "execute_result" 183 | } 184 | ], 185 | "source": [ 186 | "options nosource nonotes;\n", 187 | "proc import \n", 188 | " datafile='adult.data'\n", 189 | " dbms=csv out=df replace;\n", 190 | " getnames=no;\n", 191 | "run;\n", 192 | "options source notes;" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": null, 198 | "metadata": { 199 | "collapsed": true 200 | }, 201 | "outputs": [], 202 | "source": [] 203 | } 204 | ], 205 | "metadata": { 206 | "kernelspec": { 207 | "display_name": "SAS", 208 | "language": "sas", 209 | "name": "sas" 210 | }, 211 | "language_info": { 212 | "codemirror_mode": "sas", 213 | "file_extension": ".sas", 214 | "mimetype": "text/x-sas", 215 | "name": "sas" 216 | } 217 | }, 218 | "nbformat": 4, 219 | "nbformat_minor": 0 220 | } 221 | -------------------------------------------------------------------------------- /notebook/loadSASExtensions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# This notebook will help you install the SAS NBExtensions\n", 8 | "The process includes a mix of command line and python code and can be done either systemwide or for the current user depending on the permission level the user has." 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "This code will import the notebook module and display the paths that Juypter will search for NBExtensions" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": { 22 | "collapsed": false 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "import notebook\n", 27 | "from __future__ import print_function\n", 28 | "from jupyter_core.paths import jupyter_data_dir, jupyter_path\n", 29 | "print(jupyter_data_dir())\n", 30 | "print(jupyter_path())" 31 | ] 32 | }, 33 | { 34 | "cell_type": "markdown", 35 | "metadata": {}, 36 | "source": [ 37 | "# To Install Systemwide\n", 38 | "## This will require you to be root or have `sudo` rights " 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "The `!` below is a \"magic\" that runs os commands. The command below assumes your cuurent working directory is inside the sas_kernel cloned repository. If that is not the case then you can either:\n", 46 | "1. change the path below\n", 47 | "1. use the `!` to change the current working directory\n", 48 | "\n", 49 | "If this command is successful you should see a note about files being copied from the sas_kernel location to `/usr/local/share/jupyter/nbextensions/`" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": null, 55 | "metadata": { 56 | "collapsed": false 57 | }, 58 | "outputs": [], 59 | "source": [ 60 | "! sudo jupyter nbextension install sas_kernel/sas_kernel/nbextensions/showSASLog" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "This python code will check on the nbextension in systemwide folders (user=False is the flag for this)\n", 68 | "and then enable the extension. If anything goes wrong, you'll see the `Extension not found` message" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "metadata": { 75 | "collapsed": false 76 | }, 77 | "outputs": [], 78 | "source": [ 79 | "if notebook.nbextensions.check_nbextension('showSASLog', user=False):\n", 80 | " E = notebook.nbextensions.EnableNBExtensionApp()\n", 81 | " E.enable_nbextension('showSASLog/main')\n", 82 | "else:\n", 83 | " print (\"Extension not found\")" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": { 90 | "collapsed": false 91 | }, 92 | "outputs": [], 93 | "source": [ 94 | "! sudo jupyter nbextension install /root/sas_kernel/sas_kernel/nbextensions/theme" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": { 101 | "collapsed": false 102 | }, 103 | "outputs": [], 104 | "source": [ 105 | "if notebook.nbextensions.check_nbextension('theme', user=False):\n", 106 | " E = notebook.nbextensions.EnableNBExtensionApp()\n", 107 | " E.enable_nbextension('theme/theme_selector')\n", 108 | "else:\n", 109 | " print (\"Extension not found\")" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "# To install for the Current User" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "The `!` below is a \"magic\" that runs os commands. The command below assumes your cuurent working directory is inside the sas_kernel cloned repository. If that is not the case then you can either:\n", 124 | "1. change the path below\n", 125 | "1. use the `!` to change the current working directory\n", 126 | "\n", 127 | "If this command is successful you should see a note about files being copied from the sas_kernel location to `/usr/local/share/jupyter/nbextensions/`" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": { 134 | "collapsed": true 135 | }, 136 | "outputs": [], 137 | "source": [ 138 | "! jupyter nbextension install sas_kernel/sas_kernel/nbextensions/showSASLog --user" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "This python code will check on the nbextension in user folders `~/` (user=True is the flag for this)\n", 146 | "and then enable the extension. If anything goes wrong, you'll see the `Extension not found` message" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": null, 152 | "metadata": { 153 | "collapsed": true 154 | }, 155 | "outputs": [], 156 | "source": [ 157 | "if notebook.nbextensions.check_nbextension('showSASLog', user=True):\n", 158 | " E = notebook.nbextensions.EnableNBExtensionApp()\n", 159 | " E.enable_nbextension('showSASLog/main')\n", 160 | "else:\n", 161 | " print (\"Extension not found\")" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": null, 167 | "metadata": { 168 | "collapsed": true 169 | }, 170 | "outputs": [], 171 | "source": [ 172 | "! jupyter nbextension install /root/sas_kernel/sas_kernel/nbextensions/theme --user" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": { 179 | "collapsed": true 180 | }, 181 | "outputs": [], 182 | "source": [ 183 | "if notebook.nbextensions.check_nbextension('theme', user=True):\n", 184 | " E = notebook.nbextensions.EnableNBExtensionApp()\n", 185 | " E.enable_nbextension('theme/theme_selector')\n", 186 | "else:\n", 187 | " print (\"Extension not found\")" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "metadata": {}, 193 | "source": [ 194 | "# Check to see what NBExtensions are Installed\n", 195 | "This code will produce a simple table listing the installed extensions this user has access to. It will be a mix of user and systemwide extensions if applicable.\n", 196 | "\n", 197 | "Is should list at least the following:\n", 198 | "\n", 199 | "|Extension name|\n", 200 | "|---|\n", 201 | "|showSASLog/main|\n", 202 | "|theme/theme_selector|\n" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": null, 208 | "metadata": { 209 | "collapsed": false 210 | }, 211 | "outputs": [], 212 | "source": [ 213 | "from notebook.services.config import ConfigManager\n", 214 | "from IPython.display import HTML\n", 215 | "ip = get_ipython()\n", 216 | "cm = ConfigManager(parent=ip, profile_dir=ip.profile_dir.location)\n", 217 | "extensions =cm.get('notebook')\n", 218 | "table = \"\"\n", 219 | "for ext in extensions['load_extensions']:\n", 220 | " table += \"%s\\n\" % (ext)\n", 221 | "\n", 222 | "top = \"\"\"\n", 223 | "\n", 224 | " \n", 225 | " \n", 226 | " \n", 227 | "\"\"\"\n", 228 | "bottom = \"\"\"\n", 229 | "
Extension name
\n", 230 | "\"\"\"\n", 231 | "HTML(top + table + bottom)\n" 232 | ] 233 | } 234 | ], 235 | "metadata": { 236 | "kernelspec": { 237 | "display_name": "Python 3", 238 | "language": "python", 239 | "name": "python3" 240 | }, 241 | "language_info": { 242 | "codemirror_mode": { 243 | "name": "ipython", 244 | "version": 3 245 | }, 246 | "file_extension": ".py", 247 | "mimetype": "text/x-python", 248 | "name": "python", 249 | "nbconvert_exporter": "python", 250 | "pygments_lexer": "ipython3", 251 | "version": "3.5.1" 252 | } 253 | }, 254 | "nbformat": 4, 255 | "nbformat_minor": 0 256 | } 257 | -------------------------------------------------------------------------------- /sas_kernel/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright SAS Institute 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the License); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | """SAS Kernel Juypter Implementation""" 18 | from .version import __version__ 19 | -------------------------------------------------------------------------------- /sas_kernel/__main__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright SAS Institute 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the License); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | from ipykernel.kernelapp import IPKernelApp 18 | from .kernel import SASKernel 19 | IPKernelApp.launch_instance(kernel_class=SASKernel) 20 | -------------------------------------------------------------------------------- /sas_kernel/data/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | _dataRoot = os.path.abspath(os.path.dirname(__file__)) 3 | print (_dataRoot) -------------------------------------------------------------------------------- /sas_kernel/data/logo-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/data/logo-64x64.png -------------------------------------------------------------------------------- /sas_kernel/data/sasproclist.json: -------------------------------------------------------------------------------- 1 | ["ACCESS", "ACECLUS", "ADAPTIVEREG", "ALLELE", "ANOM", "ANOVA", "APPEND", "ARIMA", "AUTHLIB", "AUTOREG", "BOM", "BOXPLOT", "BTL", "CALENDAR", "CALIS", "CALLRFC", "CANCORR", "CANDISC", "CAPABILITY", "CASECONTROL", "CATALOG", "CATMOD", "CHART", "CIMPORT", "CLP", "CLUSTER", "COMPARE", "COMPUTAB", "CONTENTS", "CONVERT", "COPY", "CORR", "CORRESP", "COUNTREG", "CPM", "CPORT", "CUSUM", "DATA", "DATASETS", "DATASOURCE", "DBF", "DBLOAD", "DEFINE_EVENT", "DEFINE_TAGSET", "DELETE", "DIF", "DISCRIM", "DISPLAY", "DISTANCE", "DOCUMENT", "DQMATCH", "DQSCHEME", "DQSRVADM", "DQSRVSVC", "DS2", "DSTRANS", "DTREE", "ENTROPY", "ESM", "EXPAND", "EXPLODE", "EXPORT", "FACTEX", "FACTOR", "FAMILY", "FASTCLUS", "FCMP", "FEDSQL", "FMM", "FONTREG", "FORECAST", "FORMAT", "FORMS", "FREQ", "FSBROWSE", "FSEDIT", "FSLETTER", "FSLIST", "FSVIEW", "G3D", "G3GRID", "GA", "GAM", "GANNO", "GANTT", "GAREABAR", "GBARLINE", "GCHART", "GCONTOUR", "GDEVICE", "GENESELECT", "GENMOD", "GEOCODE", "GFONT", "GIMPORT", "GINSIDE", "GKEYMAP", "GKPI", "GLIMMIX", "GLM", "GLMMOD", "GLMPOWER", "GLMSELECT", "GMAP", "GOPTIONS", "GPLOT", "GPROJECT", "GRADAR", "GREDUCE", "GREMOVE", "GREPLAY", "GROOVY", "GSLIDE", "GTESTIT", "GTILE", "HADOOP", "HAPLOTYPE", "HP4SCORE", "HPBIN", "HPCLUS", "HPCORR", "HPCOUNTREG", "HPDECIDE", "HPDMDB", "HPDS2", "HPF", "HPFARIMASPEC", "HPFDIAGNOSE", "HPFENGINE", "HPFESMSPEC", "HPFEVENTS", "HPFEXMSPEC", "HPFIDMSPEC", "HPFOREST", "HPFSELECT", "HPFUCMSPEC", "HPGENSELECT", "HPIMPUTE", "HPLMIXED", "HPLOGISTIC", "HPLSO", "HPMIXED", "HPNEURAL", "HPNLIN", "HPNLMOD", "HPQLIM", "HPREDUCE", "HPREG", "HPSAMPLE", "HPSEVERITY", "HPSPLIT", "HPSUMMARY", "HPTMINE", "HPTMSCORE", "HTSNP", "HTTP", "IML", "IMPORT", "IMSTAT", "INBREED", "INFOMAPS", "INTPOINT", "ISHIKAWA", "JSON", "KDE", "KRIGE2D", "LATTICE", "LIFEREG", "LIFETEST", "LOAN", "LOESS", "LOGISTIC", "LP", "MACONTROL", "MACRO", "MAPIMPORT", "MCMC", "MDC", "MDS", "MEANS", "METADATA", "MI", "MIANALYZE", "MIGRATE", "MIXED", "MODECLUS", "MODEL", "MULTTEST", "NESTED", "NETDRAW", "NETFLOW", "NLIN", "NLMIXED", "NLP", "NPAR1WAY", "ODS", "OLAP", "OPERATE", "OPTEX", "OPTGRAPH", "OPTIONS", "OPTLOAD", "OPTLP", "OPTMILP", "OPTMODEL", "OPTQP", "OPTSAVE", "ORTHOREG", "PANEL", "PARETO", "PDLREG", "PHREG", "PLAN", "PLM", "PLOT", "PLS", "PM", "PMENU", "POWER", "PRESENV", "PRINCOMP", "PRINQUAL", "PRINT", "PRINTTO", "PROBIT", "PROTO", "PRTDEF", "PRTEXP", "PSMOOTH", "PWENCODE", "QDEVICE", "QLIM", "QUANTLIFE", "QUANTREG", "QUANTSELECT", "RANK", "REG", "REGISTRY", "RELIABILITY", "REPORT", "ROBUSTREG", "RSREG", "SCAPROC", "SCORE", "SEQDESIGN", "SEQTEST", "SERVER", "SEVERITY", "SGDESIGN", "SGPANEL", "SGPLOT", "SGRENDER", "SGSCATTER", "SHEWHART", "SIM2D", "SIMILARITY", "SIMLIN", "SIMNORMAL", "SOAP", "SORT", "SPECTRA", "SQL", "STANDARD", "STATESPACE", "STATGRAPH", "STDIZE", "STDRATE", "STEPDISC", "STP", "STREAM", "SUMMARY", "SURVEYFREQ", "SURVEYLOGISTIC", "SURVEYMEANS", "SURVEYPHREG", "SURVEYREG", "SURVEYSELECT", "SYSLIN", "TABULATE", "TCALIS", "TEMPLATE", "TIMEID", "TIMEPLOT", "TIMESERIES", "TPSPLINE", "TRANS", "TRANSPOSE", "TRANSREG", "TRANTAB", "TREE", "TSCSREG", "TTEST", "UCM", "UNIVARIATE", "VARCLUS", "VARCOMP", "VARIOGRAM", "VARMAX", "X11", "X12", "XSL"] -------------------------------------------------------------------------------- /sas_kernel/doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don\'t have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " epub3 to make an epub3" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | @echo " dummy to check syntax errors of document sources" 51 | 52 | .PHONY: clean 53 | clean: 54 | rm -rf $(BUILDDIR)/* 55 | 56 | .PHONY: html 57 | html: 58 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 61 | 62 | .PHONY: dirhtml 63 | dirhtml: 64 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 65 | @echo 66 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 67 | 68 | .PHONY: singlehtml 69 | singlehtml: 70 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 71 | @echo 72 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 73 | 74 | .PHONY: pickle 75 | pickle: 76 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 77 | @echo 78 | @echo "Build finished; now you can process the pickle files." 79 | 80 | .PHONY: json 81 | json: 82 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 83 | @echo 84 | @echo "Build finished; now you can process the JSON files." 85 | 86 | .PHONY: htmlhelp 87 | htmlhelp: 88 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 89 | @echo 90 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 91 | ".hhp project file in $(BUILDDIR)/htmlhelp." 92 | 93 | .PHONY: qthelp 94 | qthelp: 95 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 96 | @echo 97 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 98 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 99 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Pipefitter.qhcp" 100 | @echo "To view the help file:" 101 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Pipefitter.qhc" 102 | 103 | .PHONY: applehelp 104 | applehelp: 105 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 106 | @echo 107 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 108 | @echo "N.B. You won't be able to view it unless you put it in" \ 109 | "~/Library/Documentation/Help or install it in your application" \ 110 | "bundle." 111 | 112 | .PHONY: devhelp 113 | devhelp: 114 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 115 | @echo 116 | @echo "Build finished." 117 | @echo "To view the help file:" 118 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Pipefitter" 119 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Pipefitter" 120 | @echo "# devhelp" 121 | 122 | .PHONY: epub 123 | epub: 124 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 125 | @echo 126 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 127 | 128 | .PHONY: epub3 129 | epub3: 130 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 131 | @echo 132 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 133 | 134 | .PHONY: latex 135 | latex: 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo 138 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 139 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 140 | "(use \`make latexpdf' here to do that automatically)." 141 | 142 | .PHONY: latexpdf 143 | latexpdf: 144 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 145 | @echo "Running LaTeX files through pdflatex..." 146 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 147 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 148 | 149 | .PHONY: latexpdfja 150 | latexpdfja: 151 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 152 | @echo "Running LaTeX files through platex and dvipdfmx..." 153 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 154 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 155 | 156 | .PHONY: text 157 | text: 158 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 159 | @echo 160 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 161 | 162 | .PHONY: man 163 | man: 164 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 165 | @echo 166 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 167 | 168 | .PHONY: texinfo 169 | texinfo: 170 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 171 | @echo 172 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 173 | @echo "Run \`make' in that directory to run these through makeinfo" \ 174 | "(use \`make info' here to do that automatically)." 175 | 176 | .PHONY: info 177 | info: 178 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 179 | @echo "Running Texinfo files through makeinfo..." 180 | make -C $(BUILDDIR)/texinfo info 181 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 182 | 183 | .PHONY: gettext 184 | gettext: 185 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 186 | @echo 187 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 188 | 189 | .PHONY: changes 190 | changes: 191 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 192 | @echo 193 | @echo "The overview file is in $(BUILDDIR)/changes." 194 | 195 | .PHONY: linkcheck 196 | linkcheck: 197 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 198 | @echo 199 | @echo "Link check complete; look for any errors in the above output " \ 200 | "or in $(BUILDDIR)/linkcheck/output.txt." 201 | 202 | .PHONY: doctest 203 | doctest: 204 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 205 | @echo "Testing of doctests in the sources finished, look at the " \ 206 | "results in $(BUILDDIR)/doctest/output.txt." 207 | 208 | .PHONY: coverage 209 | coverage: 210 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 211 | @echo "Testing of coverage in the sources finished, look at the " \ 212 | "results in $(BUILDDIR)/coverage/python.txt." 213 | 214 | .PHONY: xml 215 | xml: 216 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 217 | @echo 218 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 219 | 220 | .PHONY: pseudoxml 221 | pseudoxml: 222 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 223 | @echo 224 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 225 | 226 | .PHONY: dummy 227 | dummy: 228 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy 229 | @echo 230 | @echo "Build finished. Dummy builder generates no files." -------------------------------------------------------------------------------- /sas_kernel/doc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/__init__.py -------------------------------------------------------------------------------- /sas_kernel/doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. epub3 to make an epub3 31 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 32 | echo. text to make text files 33 | echo. man to make manual pages 34 | echo. texinfo to make Texinfo files 35 | echo. gettext to make PO message catalogs 36 | echo. changes to make an overview over all changed/added/deprecated items 37 | echo. xml to make Docutils-native XML files 38 | echo. pseudoxml to make pseudoxml-XML files for display purposes 39 | echo. linkcheck to check all external links for integrity 40 | echo. doctest to run all doctests embedded in the documentation if enabled 41 | echo. coverage to run coverage check of the documentation if enabled 42 | echo. dummy to check syntax errors of document sources 43 | goto end 44 | ) 45 | 46 | if "%1" == "clean" ( 47 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 48 | del /q /s %BUILDDIR%\* 49 | goto end 50 | ) 51 | 52 | 53 | REM Check if sphinx-build is available and fallback to Python version if any 54 | %SPHINXBUILD% 1>NUL 2>NUL 55 | if errorlevel 9009 goto sphinx_python 56 | goto sphinx_ok 57 | 58 | :sphinx_python 59 | 60 | set SPHINXBUILD=python -m sphinx.__init__ 61 | %SPHINXBUILD% 2> nul 62 | if errorlevel 9009 ( 63 | echo. 64 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 65 | echo.installed, then set the SPHINXBUILD environment variable to point 66 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 67 | echo.may add the Sphinx directory to PATH. 68 | echo. 69 | echo.If you don't have Sphinx installed, grab it from 70 | echo.http://sphinx-doc.org/ 71 | exit /b 1 72 | ) 73 | 74 | :sphinx_ok 75 | 76 | 77 | if "%1" == "html" ( 78 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 79 | if errorlevel 1 exit /b 1 80 | echo. 81 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 82 | goto end 83 | ) 84 | 85 | if "%1" == "dirhtml" ( 86 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 87 | if errorlevel 1 exit /b 1 88 | echo. 89 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 90 | goto end 91 | ) 92 | 93 | if "%1" == "singlehtml" ( 94 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 95 | if errorlevel 1 exit /b 1 96 | echo. 97 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 98 | goto end 99 | ) 100 | 101 | if "%1" == "pickle" ( 102 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 103 | if errorlevel 1 exit /b 1 104 | echo. 105 | echo.Build finished; now you can process the pickle files. 106 | goto end 107 | ) 108 | 109 | if "%1" == "json" ( 110 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 111 | if errorlevel 1 exit /b 1 112 | echo. 113 | echo.Build finished; now you can process the JSON files. 114 | goto end 115 | ) 116 | 117 | if "%1" == "htmlhelp" ( 118 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 119 | if errorlevel 1 exit /b 1 120 | echo. 121 | echo.Build finished; now you can run HTML Help Workshop with the ^ 122 | .hhp project file in %BUILDDIR%/htmlhelp. 123 | goto end 124 | ) 125 | 126 | if "%1" == "qthelp" ( 127 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 128 | if errorlevel 1 exit /b 1 129 | echo. 130 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 131 | .qhcp project file in %BUILDDIR%/qthelp, like this: 132 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Pipefitter.qhcp 133 | echo.To view the help file: 134 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Pipefitter.ghc 135 | goto end 136 | ) 137 | 138 | if "%1" == "devhelp" ( 139 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 140 | if errorlevel 1 exit /b 1 141 | echo. 142 | echo.Build finished. 143 | goto end 144 | ) 145 | 146 | if "%1" == "epub" ( 147 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 148 | if errorlevel 1 exit /b 1 149 | echo. 150 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 151 | goto end 152 | ) 153 | 154 | if "%1" == "epub3" ( 155 | %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 156 | if errorlevel 1 exit /b 1 157 | echo. 158 | echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. 159 | goto end 160 | ) 161 | 162 | if "%1" == "latex" ( 163 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 164 | if errorlevel 1 exit /b 1 165 | echo. 166 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdf" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "latexpdfja" ( 181 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 182 | cd %BUILDDIR%/latex 183 | make all-pdf-ja 184 | cd %~dp0 185 | echo. 186 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 187 | goto end 188 | ) 189 | 190 | if "%1" == "text" ( 191 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 192 | if errorlevel 1 exit /b 1 193 | echo. 194 | echo.Build finished. The text files are in %BUILDDIR%/text. 195 | goto end 196 | ) 197 | 198 | if "%1" == "man" ( 199 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 200 | if errorlevel 1 exit /b 1 201 | echo. 202 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 203 | goto end 204 | ) 205 | 206 | if "%1" == "texinfo" ( 207 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 208 | if errorlevel 1 exit /b 1 209 | echo. 210 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 211 | goto end 212 | ) 213 | 214 | if "%1" == "gettext" ( 215 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 216 | if errorlevel 1 exit /b 1 217 | echo. 218 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 219 | goto end 220 | ) 221 | 222 | if "%1" == "changes" ( 223 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 224 | if errorlevel 1 exit /b 1 225 | echo. 226 | echo.The overview file is in %BUILDDIR%/changes. 227 | goto end 228 | ) 229 | 230 | if "%1" == "linkcheck" ( 231 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 232 | if errorlevel 1 exit /b 1 233 | echo. 234 | echo.Link check complete; look for any errors in the above output ^ 235 | or in %BUILDDIR%/linkcheck/output.txt. 236 | goto end 237 | ) 238 | 239 | if "%1" == "doctest" ( 240 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 241 | if errorlevel 1 exit /b 1 242 | echo. 243 | echo.Testing of doctests in the sources finished, look at the ^ 244 | results in %BUILDDIR%/doctest/output.txt. 245 | goto end 246 | ) 247 | 248 | if "%1" == "coverage" ( 249 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 250 | if errorlevel 1 exit /b 1 251 | echo. 252 | echo.Testing of coverage in the sources finished, look at the ^ 253 | results in %BUILDDIR%/coverage/python.txt. 254 | goto end 255 | ) 256 | 257 | if "%1" == "xml" ( 258 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 259 | if errorlevel 1 exit /b 1 260 | echo. 261 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 262 | goto end 263 | ) 264 | 265 | if "%1" == "pseudoxml" ( 266 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 267 | if errorlevel 1 exit /b 1 268 | echo. 269 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 270 | goto end 271 | ) 272 | 273 | if "%1" == "dummy" ( 274 | %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy 275 | if errorlevel 1 exit /b 1 276 | echo. 277 | echo.Build finished. Dummy builder generates no files. 278 | goto end 279 | ) 280 | 281 | :end -------------------------------------------------------------------------------- /sas_kernel/doc/source/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/__init__.py -------------------------------------------------------------------------------- /sas_kernel/doc/source/api.rst: -------------------------------------------------------------------------------- 1 | 2 | .. Copyright SAS Institute 3 | 4 | .. currentmodule:: sas_kernel 5 | 6 | .. _api: 7 | 8 | ************* 9 | API Reference 10 | ************* 11 | 12 | .. automodule:: sas_kernel.kernel 13 | :members: 14 | :undoc-members: 15 | :inherited-members: 16 | :show-inheritance: 17 | 18 | SAS Kernel Object 19 | ----------------- 20 | .. autoclass:: SASKernel 21 | :members: -------------------------------------------------------------------------------- /sas_kernel/doc/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # SAS kernel documentation build configuration file, created by 5 | # sphinx-quickstart on Fri Oct 28 10:08:55 2016. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | import sys 17 | import os 18 | import sas_kernel 19 | 20 | # If extensions (or modules to document with autodoc) are in another directory, 21 | # add these directories to sys.path here. If the directory is relative to the 22 | # documentation root, use os.path.abspath to make it absolute, like shown here. 23 | sys.path.insert(0, os.path.abspath('.')) 24 | 25 | # -- General configuration ------------------------------------------------ 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | #needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = ['sphinx.ext.autodoc', 34 | 'sphinx.ext.autosummary', 35 | 'sphinx.ext.doctest', 36 | 'sphinx.ext.extlinks', 37 | 'sphinx.ext.todo', 38 | 'numpydoc', # used to parse numpy-style docstrings for autodoc 39 | 'IPython.sphinxext.ipython_directive', 40 | 'IPython.sphinxext.ipython_console_highlighting', 41 | 'sphinx.ext.intersphinx', 42 | 'sphinx.ext.coverage', 43 | 'sphinx.ext.mathjax', 44 | 'sphinx.ext.ifconfig', 45 | 'sphinx.ext.githubpages', 46 | ] 47 | 48 | autosummary_generate = True 49 | numpydoc_show_class_members = False 50 | autodoc_default_flags = ['show-inheritance'] 51 | autoclass_content = 'class' 52 | 53 | intersphinx_mapping = {'python': ('https://docs.python.org/', None), 54 | 'pandas': ('http://pandas.pydata.org/pandas-docs/stable/', None), 55 | 'numpy': ('http://docs.scipy.org/doc/numpy/', None), 56 | 'scipy': ('http://docs.scipy.org/doc/scipy/reference/', None), 57 | 'matplotlib': ('http://matplotlib.sourceforge.net/', None)} 58 | 59 | # Add any paths that contain templates here, relative to this directory. 60 | templates_path = ['_templates'] 61 | 62 | # The suffix(es) of source filenames. 63 | # You can specify multiple suffix as a list of string: 64 | # source_suffix = ['.rst', '.md'] 65 | source_suffix = '.rst' 66 | 67 | # The encoding of source files. 68 | #source_encoding = 'utf-8-sig' 69 | 70 | # The master toctree document. 71 | master_doc = 'index' 72 | 73 | # General information about the project. 74 | project = 'sas_kernel' 75 | # copyright = '2017 SAS Institute Inc. All Rights Reserved.' 76 | author = 'Jared Dean and Tom Weber' 77 | 78 | # The version info for the project you're documenting, acts as replacement for 79 | # |version| and |release|, also used in various other places throughout the 80 | # built documents. 81 | # 82 | 83 | 84 | 85 | # The short X.Y version. 86 | version = sas_kernel.__version__ 87 | # The full version, including alpha/beta/rc tags. 88 | release = sas_kernel.__version__ 89 | 90 | # The language for content autogenerated by Sphinx. Refer to documentation 91 | # for a list of supported languages. 92 | # 93 | # This is also used if you do content translation via gettext catalogs. 94 | # Usually you set "language" from the command line for these cases. 95 | language = None 96 | 97 | # There are two options for replacing |today|: either, you set today to some 98 | # non-false value, then it is used: 99 | #today = '' 100 | # Else, today_fmt is used as the format for a strftime call. 101 | #today_fmt = '%B %d, %Y' 102 | 103 | # List of patterns, relative to source directory, that match files and 104 | # directories to ignore when looking for source files. 105 | # This patterns also effect to html_static_path and html_extra_path 106 | exclude_patterns = [] 107 | 108 | # The reST default role (used for this markup: `text`) to use for all 109 | # documents. 110 | #default_role = None 111 | 112 | # If true, '()' will be appended to :func: etc. cross-reference text. 113 | #add_function_parentheses = True 114 | 115 | # If true, the current module name will be prepended to all description 116 | # unit titles (such as .. function::). 117 | #add_module_names = True 118 | 119 | # If true, sectionauthor and moduleauthor directives will be shown in the 120 | # output. They are ignored by default. 121 | #show_authors = False 122 | 123 | # The name of the Pygments (syntax highlighting) style to use. 124 | pygments_style = 'sphinx' 125 | 126 | # A list of ignored prefixes for module index sorting. 127 | #modindex_common_prefix = [] 128 | 129 | # If true, keep warnings as "system message" paragraphs in the built documents. 130 | #keep_warnings = False 131 | 132 | # If true, `todo` and `todoList` produce output, else they produce nothing. 133 | todo_include_todos = False 134 | 135 | 136 | # -- Options for HTML output ---------------------------------------------- 137 | 138 | # The theme to use for HTML and HTML Help pages. See the documentation for 139 | # a list of builtin themes. 140 | html_theme = 'sphinx_rtd_theme' 141 | 142 | html_context = { 143 | 'css_files': ['_static/custom.css'], 144 | } 145 | 146 | # Theme options are theme-specific and customize the look and feel of a theme 147 | # further. For a list of options available for each theme, see the 148 | # documentation. 149 | #html_theme_options = {} 150 | 151 | import sphinx_rtd_theme 152 | 153 | # Add any paths that contain custom themes here, relative to this directory. 154 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 155 | 156 | # The name for this set of Sphinx documents. 157 | # " v documentation" by default. 158 | #html_title = 'Pipefitter v0.1.0' 159 | 160 | # A shorter title for the navigation bar. Default is the same as html_title. 161 | #html_short_title = None 162 | 163 | # The name of an image file (relative to this directory) to place at the top 164 | # of the sidebar. 165 | #html_logo = None 166 | 167 | # The name of an image file (relative to this directory) to use as a favicon of 168 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 169 | # pixels large. 170 | #html_favicon = None 171 | 172 | # Add any paths that contain custom static files (such as style sheets) here, 173 | # relative to this directory. They are copied after the builtin static files, 174 | # so a file named "default.css" will overwrite the builtin "default.css". 175 | html_static_path = ['_static'] 176 | 177 | # Add any extra paths that contain custom files (such as robots.txt or 178 | # .htaccess) here, relative to this directory. These files are copied 179 | # directly to the root of the documentation. 180 | #html_extra_path = [] 181 | 182 | # If not None, a 'Last updated on:' timestamp is inserted at every page 183 | # bottom, using the given strftime format. 184 | # The empty string is equivalent to '%b %d, %Y'. 185 | #html_last_updated_fmt = None 186 | 187 | # If true, SmartyPants will be used to convert quotes and dashes to 188 | # typographically correct entities. 189 | #html_use_smartypants = True 190 | 191 | # Custom sidebar templates, maps document names to template names. 192 | #html_sidebars = {} 193 | 194 | # Additional templates that should be rendered to pages, maps page names to 195 | # template names. 196 | #html_additional_pages = {} 197 | 198 | # If false, no module index is generated. 199 | #html_domain_indices = True 200 | 201 | # If false, no index is generated. 202 | #html_use_index = True 203 | 204 | # If true, the index is split into individual pages for each letter. 205 | #html_split_index = False 206 | 207 | # If true, links to the reST sources are added to the pages. 208 | #html_show_sourcelink = True 209 | 210 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 211 | #html_show_sphinx = True 212 | 213 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 214 | #html_show_copyright = True 215 | 216 | # If true, an OpenSearch description file will be output, and all pages will 217 | # contain a tag referring to it. The value of this option must be the 218 | # base URL from which the finished HTML is served. 219 | #html_use_opensearch = '' 220 | 221 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 222 | #html_file_suffix = None 223 | 224 | # Language to be used for generating the HTML full-text search index. 225 | # Sphinx supports the following languages: 226 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 227 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' 228 | #html_search_language = 'en' 229 | 230 | # A dictionary with options for the search language support, empty by default. 231 | # 'ja' uses this config value. 232 | # 'zh' user can custom change `jieba` dictionary path. 233 | #html_search_options = {'type': 'default'} 234 | 235 | # The name of a javascript file (relative to the configuration directory) that 236 | # implements a search results scorer. If empty, the default will be used. 237 | #html_search_scorer = 'scorer.js' 238 | 239 | # Output file base name for HTML help builder. 240 | htmlhelp_basename = 'Pipefitterdoc' 241 | 242 | # -- Options for LaTeX output --------------------------------------------- 243 | 244 | latex_elements = { 245 | # The paper size ('letterpaper' or 'a4paper'). 246 | #'papersize': 'letterpaper', 247 | 248 | # The font size ('10pt', '11pt' or '12pt'). 249 | #'pointsize': '10pt', 250 | 251 | # Additional stuff for the LaTeX preamble. 252 | #'preamble': '', 253 | 254 | # Latex figure (float) alignment 255 | #'figure_align': 'htbp', 256 | } 257 | 258 | # Grouping the document tree into LaTeX files. List of tuples 259 | # (source start file, target name, title, 260 | # author, documentclass [howto, manual, or own class]). 261 | latex_documents = [ 262 | (master_doc, 'sas_kernel.tex', 'SAS Kernel for Jupyter Notebook', 263 | 'SAS Institute Inc.', 'manual'), 264 | ] 265 | 266 | # The name of an image file (relative to this directory) to place at the top of 267 | # the title page. 268 | #latex_logo = None 269 | 270 | # For "manual" documents, if this is true, then toplevel headings are parts, 271 | # not chapters. 272 | #latex_use_parts = False 273 | 274 | # If true, show page references after internal links. 275 | #latex_show_pagerefs = False 276 | 277 | # If true, show URL addresses after external links. 278 | #latex_show_urls = False 279 | 280 | # Documents to append as an appendix to all manuals. 281 | #latex_appendices = [] 282 | 283 | # If false, no module index is generated. 284 | #latex_domain_indices = True 285 | 286 | 287 | # -- Options for manual page output --------------------------------------- 288 | 289 | # One entry per manual page. List of tuples 290 | # (source start file, name, description, authors, manual section). 291 | man_pages = [ 292 | (master_doc, 'sas_kernel', 'SAS Kernel for Jupyter Notebook', 293 | [author], 1) 294 | ] 295 | 296 | # If true, show URL addresses after external links. 297 | #man_show_urls = False 298 | 299 | 300 | # -- Options for Texinfo output ------------------------------------------- 301 | 302 | # Grouping the document tree into Texinfo files. List of tuples 303 | # (source start file, target name, title, author, 304 | # dir menu entry, description, category) 305 | texinfo_documents = [ 306 | (master_doc, 'sas_kernel', 'SAS Kernel for Jupyter Notebook', 307 | author, 'sas_kernel', 'Provides a SAS kernel for use with Jupyter Notebook.', 308 | 'Miscellaneous'), 309 | ] 310 | 311 | # Documents to append as an appendix to all manuals. 312 | #texinfo_appendices = [] 313 | 314 | # If false, no module index is generated. 315 | #texinfo_domain_indices = True 316 | 317 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 318 | #texinfo_show_urls = 'footnote' 319 | 320 | # If true, do not generate a @detailmenu in the "Top" node's menu. 321 | #texinfo_no_detailmenu = False 322 | 323 | 324 | # Example configuration for intersphinx: refer to the Python standard library. 325 | intersphinx_mapping = {'https://docs.python.org/': None} 326 | -------------------------------------------------------------------------------- /sas_kernel/doc/source/getting-started.rst: -------------------------------------------------------------------------------- 1 | ############### 2 | Getting started 3 | ############### 4 | 5 | The SAS kernel for Juypter is designed to enable users to write programs 6 | for SAS with Jupyter Notebooks. The kernel makes SAS the analytical engine 7 | or "calculator" for data analysis. 8 | 9 | The following SAS program is a basic example of programming with SAS 10 | and Jupyter Notebook. The HR_comma_sep.csv file that is referenced in 11 | the sample code is available from https://www.kaggle.com/ludobenistant/hr-analytics. 12 | 13 | 14 | ****************** 15 | Load data into SAS 16 | ****************** 17 | 18 | The FILENAME statement is used to specify an external file. The 19 | IMPORT procedure can read data from a variety of external file formats. 20 | 21 | .. code-block:: none 22 | 23 | filename x "./HR_comma_sep.csv"; 24 | proc import datafile=x out=_csv dbms=csv replace; run; 25 | 26 | 27 | **************** 28 | Explore the data 29 | **************** 30 | 31 | The CONTENTS procedure can be used to display the column names and 32 | data types in a SAS data set. The PRINT procedure can be used to 33 | display rows from a data set. In this case, the results are limited to 34 | the first five rows. 35 | 36 | .. code-block:: none 37 | 38 | proc contents data=work._csv; 39 | ods select Variables; 40 | run; 41 | 42 | proc print data=work._csv(obs=5); 43 | run; 44 | 45 | The MEANS procedure is used to provide descriptive statistics. 46 | 47 | .. code-block:: none 48 | 49 | proc means data=work._csv n nmiss median mean std min p25 p50 p75 max; 50 | run; 51 | 52 | 53 | The SGPLOT procedure is used to produce a vertical bar charts of frequencies 54 | for salary and sales. 55 | 56 | .. code-block:: none 57 | 58 | proc sgplot data=work._csv; 59 | vbar salary; 60 | xaxis discreteorder=data; 61 | run; 62 | 63 | proc sgplot data=work._csv; 64 | vbar sales; 65 | run; 66 | 67 | .. image:: ./images/sgplot_vbar_salary.png 68 | :scale: 60 % 69 | :alt: Plot of low, medium, and high salary frequencies. 70 | 71 | The plot of sales across groups, such as IT, RandD, and so on is similar. 72 | 73 | 74 | Plot histograms with normal distribution curves. 75 | 76 | .. code-block:: none 77 | 78 | proc sgplot data=work._csv; 79 | histogram satisfaction_level / scale=count; 80 | density satisfaction_level; 81 | run; 82 | 83 | proc sgplot data=work._csv; 84 | histogram time_spend_company / scale=count; 85 | density time_spend_company; 86 | run; 87 | 88 | proc sgplot data=work._csv; 89 | histogram last_evaluation / scale=count; 90 | density last_evaluation; 91 | run; 92 | 93 | .. image:: ./images/hist_satisfaction_level.png 94 | :scale: 60 % 95 | :alt: Histogram of employee satisfaction. 96 | 97 | The plots for time spent with the company and last evaluation 98 | are similar. 99 | 100 | Plot a heatmap that shows the relationship between employee 101 | satisfaction and the last evaluation. 102 | 103 | .. code-block:: none 104 | 105 | proc sgplot data=work._csv; 106 | heatmap x=last_evaluation y=satisfaction_level; 107 | run; 108 | 109 | .. image:: ./images/heatmap_satisfaction_evaluation.png 110 | :scale: 60 % 111 | :alt: Heatmap of employee statisfaction and evaluation. 112 | 113 | There is a small frequency spike in the lower-right corner 114 | of the heatmap. 115 | 116 | Narrow the heatmap to the employees that have low satisfaction 117 | but were evaluated highly. 118 | 119 | .. code-block:: none 120 | 121 | proc sgplot data=work._csv(where=(satisfaction_level <.2 and last_evaluation >. 7)); 122 | heatmap x=last_evaluation y=satisfaction_level; 123 | run; 124 | 125 | 126 | Finally, split the median satisfaction level for retained employees 127 | side-by-side with the median satisfaction for employees who left. 128 | 129 | .. code-block:: none 130 | 131 | proc sgpanel data=work._csv; 132 | *where satisfaction_level <.2 and last_evaluation > .7; 133 | PANELBY left; 134 | hbar sales / response=last_evaluation stat=median; 135 | hbar sales / response=satisfaction_level stat=median ; 136 | run; 137 | 138 | .. tip:: You can remove the asterisk to plot the employees with 139 | low satisfaction and high evaluations. 140 | 141 | .. image:: ./images/panel_left_sales.png 142 | :scale: 60 % 143 | :alt: Heatmap of employee statisfaction and evaluation for 144 | employees with low satisfaction and high evaluation. 145 | 146 | 147 | 148 | ************************************* 149 | Split the data into training and test 150 | ************************************* 151 | 152 | Replace the data set with one that includes a partitioning indicator. 153 | The variable is named _PartInd_ and indicates whether the partition 154 | is part of the training data (1) or the test data (0). Seventy percent 155 | of the data is included in training and the remainder is used for testing. 156 | 157 | .. code-block:: none 158 | 159 | proc hpsample data=work._csv out=work._csv samppct=70 seed=9878 partition; 160 | class left _character_; 161 | target left; 162 | var work_accident average_montly_hours last_evaluation number_project 163 | promotion_last_5years satisfaction_level time_spend_company; 164 | run; 165 | 166 | 167 | ************************ 168 | Train a series of models 169 | ************************ 170 | 171 | Decision tree 172 | ============= 173 | 174 | The HPSPLIT procedure can train a decision tree model. For documentation, 175 | see 176 | http://documentation.sas.com/?docsetId=statug&docsetVersion=14.2&docsetTarget=statug_hpsplit_toc.htm. 177 | 178 | .. code-block:: none 179 | 180 | proc hpsplit data=work._csv(where=(_partInd_=1)) plot=all; 181 | class work_accident promotion_last_5years sales salary; 182 | model left = work_accident promotion_last_5years sales salary 183 | satisfaction_level time_spend_company number_project 184 | average_montly_hours; 185 | run; 186 | 187 | The results include several tables and plots. Only the variable 188 | information table is shown below. 189 | 190 | .. image:: ./images/hpsplit_variable_importance.png 191 | :scale: 60 % 192 | :alt: Variable importance for modeling 'left' with the HPSPLIT procedure. 193 | 194 | 195 | Generalized linear model 196 | ======================== 197 | 198 | The GLM procedure can train a generalized linear model. For documentation, 199 | see 200 | http://documentation.sas.com/?docsetId=statug&docsetVersion=14.2&docsetTarget=statug_glm_toc.htm. 201 | 202 | 203 | .. code-block:: none 204 | 205 | proc glm data=work._csv(where=(_partInd_=1)) plot=all; 206 | class work_accident promotion_last_5years sales salary; 207 | model left = work_accident promotion_last_5years sales salary 208 | satisfaction_level time_spend_company number_project 209 | average_montly_hours; 210 | run; 211 | 212 | The results include several tables. Only the basic statistics and Type I sum of 213 | squares are shown. 214 | 215 | .. image:: ./images/glm_rsq_ss1.png 216 | :scale: 60 % 217 | :alt: R-square, basic statistics, and Type I sum of squares. 218 | 219 | Logistic regression 220 | =================== 221 | 222 | The HPLOGISTIC procedure can create logistic regression models. For documentation, see 223 | http://documentation.sas.com/?docsetId=statug&docsetVersion=14.2&docsetTarget=statug_glm_overview.htm. 224 | 225 | .. code-block:: none 226 | 227 | proc hplogistic data=work._csv(where=(_partInd_=1)); 228 | class work_accident promotion_last_5years sales salary; 229 | model left (event='1') = work_accident promotion_last_5years sales salary 230 | satisfaction_level time_spend_company number_project 231 | average_montly_hours; 232 | 233 | run; 234 | 235 | Neural network 236 | ============== 237 | 238 | The HPNERUAL procedure is available with a SAS Enterprise Miner license. 239 | 240 | The procedure trains a multilayer preceptron neural network. For documentation, see 241 | http://documentation.sas.com/?docsetId=emhpprcref&docsetVersion=14.2&docsetTarget=emhpprcref_hpneural_toc.htm. 242 | 243 | .. code-block:: none 244 | 245 | proc hpneural data=work._csv; 246 | hidden 19; 247 | input work_accident promotion_last_5years sales salary 248 | / level=nominal; 249 | input satisfaction_level time_spend_company number_project average_montly_hours 250 | / level=interval; 251 | target left / level=nominal; 252 | train numtries=15 maxiter=300; 253 | run; 254 | 255 | The results include several tables. The fit statistics and misclassification tables 256 | are shown below. 257 | 258 | .. image:: ./images/neural_fit_misclass.png 259 | :scale: 60 % 260 | :alt: Fit statistics and misclassification information. 261 | 262 | Decision forest 263 | =============== 264 | 265 | The HPFOREST procedure is available with a SAS Enterprise Miner license. 266 | 267 | The HPFOREST procedure creates a forest of many decision trees and creates a predictive 268 | model. For documentation, see http://documentation.sas.com/?docsetId=emhpprcref&docsetVersion=14.2&docsetTarget=emhpprcref_hpforest_toc.htm. 269 | 270 | .. code-block:: none 271 | 272 | proc hpforest data=work._csv; 273 | input work_accident promotion_last_5years sales salary 274 | / level=nominal; 275 | input satisfaction_level time_spend_company number_project average_montly_hours 276 | / level=interval; 277 | target left / level=nominal; 278 | run; 279 | 280 | The results include several tables of information. The loss reduction and variable 281 | importance table is shown below. 282 | 283 | .. image:: ./images/forest_loss_reduction.png 284 | :scale: 60 % 285 | :alt: Loss reduction and variable importance for the HPFOREST procedure. 286 | 287 | -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/ap1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/ap1.PNG -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/ap2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/ap2.PNG -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/ap3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/ap3.PNG -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/ap4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/ap4.PNG -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/ap5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/ap5.PNG -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/forest_loss_reduction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/forest_loss_reduction.png -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/glm_rsq_ss1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/glm_rsq_ss1.png -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/heatmap_satisfaction_evaluation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/heatmap_satisfaction_evaluation.png -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/hist_satisfaction_level.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/hist_satisfaction_level.png -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/hpsplit_variable_importance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/hpsplit_variable_importance.png -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/neural_fit_misclass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/neural_fit_misclass.png -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/panel_left_sales.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/panel_left_sales.png -------------------------------------------------------------------------------- /sas_kernel/doc/source/images/sgplot_vbar_salary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/doc/source/images/sgplot_vbar_salary.png -------------------------------------------------------------------------------- /sas_kernel/doc/source/index.rst: -------------------------------------------------------------------------------- 1 | :tocdepth: 4 2 | 3 | ********** 4 | SAS Kernel 5 | ********** 6 | 7 | .. module:: sas_kernel 8 | 9 | **Date**: |today| **Version**: |version| 10 | 11 | **Binary Installers:** ``_ 12 | 13 | **Source Repository:** ``_ 14 | 15 | **Issues and Ideas:** ``_ 16 | 17 | The SAS Kernel project provides a kernel for `Jupyter Notebooks `__. 18 | The kernel makes it possible to use Jupyter for writing and maintaining SAS coding projects. 19 | 20 | .. toctree:: 21 | overview 22 | install 23 | getting-started 24 | api 25 | license 26 | -------------------------------------------------------------------------------- /sas_kernel/doc/source/install.rst: -------------------------------------------------------------------------------- 1 | 2 | .. Copyright SAS Institute 3 | 4 | 5 | ========================= 6 | Installing the SAS kernel 7 | ========================= 8 | 9 | The SAS kernel package installs just like any other Python package. 10 | It is a pure Python package and works with Python 3.X 11 | installations. To install using ``pip``, you execute one of the 12 | following commands. 13 | 14 | :: 15 | 16 | pip install sas_kernel 17 | pip install http://github.com/sassoftware/sas_kernel/releases/sas_kernel-X.X.X.tar.gz 18 | 19 | 20 | ****************************************************************** 21 | Linux install for Anaconda Python (assuming SAS already installed) 22 | ****************************************************************** 23 | 24 | #. Go to https://www.continuum.io/downloads and install 25 | Anaconda Python (make sure you get Python3.X). If you install 26 | Anaconda without Superuser privileges (root or sudo), then other users 27 | on the system will not be able to access the SAS kernel. Consider the 28 | following: 29 | 30 | * The default installation location is your home directory. This is 31 | fine for a single user install. If you want a system-wide installation, 32 | then use a common location such as ``/opt``. 33 | 34 | * One installation prompt is to add the path to your environment. SAS 35 | recommends that you answer 'yes' to the prompt so that you get the 36 | executables in your path automatically. If you are performing a system-wide 37 | installation (using root or sudo), then all the other users must add 38 | the path to their environmental variables. 39 | 40 | #. Install the SAS kernel package. The package has a dependency on the SASPy 41 | package. The SASPy package is available from https://github.com/sassoftware/saspy. 42 | If the ``pip`` command in your path does not map to Python3, then use ``pip3`` 43 | instead. 44 | :: 45 | 46 | pip install sas_kernel 47 | 48 | #. Verify that the package is installed. 49 | :: 50 | 51 | jupyter kernelspec list 52 | 53 | If you installed as a Superuser, your output should look similar to the following: 54 | :: 55 | 56 | Available kernels: 57 | python3 /opt/Anaconda3-2.5.0/lib/python3.5/site-packages/ipykernel/resources 58 | sas /usr/local/share/jupyter/kernels/sas 59 | 60 | If you installed as a regular user (the sas user account, in this case), your output 61 | should look similar to the following: 62 | :: 63 | 64 | Available kernels: 65 | python3 /home/sas/anaconda3/lib/python3.5/site-packages/ipykernel/resources 66 | sas /home/sas/.local/share/jupyter/kernels/sas 67 | 68 | #. Configure the SAS executable for your system. 69 | 70 | The connection of the Jupyter notebook to SAS is made by the SASPy Python package (which was installed as a dependency 71 | for you). 72 | 73 | 74 | Use the `SASPy configuration documentation`_ to complete you set up. 75 | 76 | .. _SASPy configuration documentation: https://sassoftware.github.io/saspy/configuration.html 77 | 78 | * SASPy documentation: https://sassoftware.github.io/saspy/index.html 79 | * More information about SASPy: https://github.com/sassoftware/saspy 80 | 81 | 82 | *********************************************************** 83 | Linux install for Centos 6 (assuming SAS already installed) 84 | *********************************************************** 85 | 86 | These instructions describe hot to perform a system-wide installation for all users. 87 | You must have Superuser privileges (root or sudo). 88 | 89 | #. You can use the ``yum`` command to install from RPM packages. 90 | :: 91 | 92 | sudo yum install https://centos6.iuscommunity.org/ius-release.rpm 93 | sudo yum install python35u gcc-c++ python35u-devel python35u-pip python35u-tools 94 | 95 | #. You can use the ``pip`` command. 96 | :: 97 | 98 | wget https://bootstrap.pypa.io/get-pip.py 99 | python3.5 get-pip.py 100 | pip3 --version`` 101 | 102 | #. Install Jupyter and the SAS kernel package. The package has a dependency on the SASPy 103 | package. The SASPy package is available from https://github.com/sassoftware/saspy. 104 | :: 105 | 106 | pip3.5 install jupyter 107 | pip3.5 install sas_kernel 108 | 109 | #. Verify that the SAS kernel package is installed. 110 | :: 111 | 112 | jupyter kernelspec list 113 | 114 | Your output should look similar to the following: 115 | :: 116 | 117 | Available kernels: 118 | python3 /usr/lib/python3.5/site-packages/ipykernel/resources 119 | sas /usr/local/share/jupyter/kernels/sas 120 | 121 | #. Configure the SAS executable for your system. 122 | 123 | The connection of the Jupyter notebook to SAS is made by the SASPy Python package (which was installed as a dependency 124 | for you). 125 | 126 | 127 | Use the `SASPy configuration documentation`_ to complete you set up. 128 | 129 | .. _SASPy configuration documentation: https://sassoftware.github.io/saspy/configuration.html 130 | 131 | * SASPy documentation: https://sassoftware.github.io/saspy/index.html 132 | * More information about SASPy: https://github.com/sassoftware/saspy 133 | 134 | 135 | ************************************************ 136 | Windows install (assuming SAS already installed) 137 | ************************************************ 138 | 139 | #. Go to https://www.continuum.io/downloads and install 140 | Anaconda Python (make sure you get Python3.X). If you install 141 | Anaconda without Administrator privileges, then other users 142 | on the system will not be able to access the SAS kernel. Consider the 143 | following: 144 | 145 | * Install in the default location unless you have a good reason to change it. 146 | Using the default location simplifies administration. 147 | 148 | .. image:: ./images/ap3.PNG 149 | :scale: 50% 150 | 151 | * One installation prompt is to make Python accessible for just your account 152 | or for all users. Select the best response for your situation. 153 | 154 | .. image:: ./images/ap2.PNG 155 | :scale: 50% 156 | 157 | * Another installation prompt is to add the path to your environment. SAS 158 | recommends that you answer 'yes' to the prompt so that you get the 159 | executables in your path automatically. Adding the path your environment 160 | simplifies starting Python and Jupyter. 161 | 162 | .. image:: ./images/ap4.PNG 163 | :scale: 50% 164 | 165 | 166 | .. IMPORTANT:: 167 | 168 | This next group of steps is performed from a Windows command prompt ( 169 | :menuselection:`Start --> Run --> cmd`) 170 | 171 | #. Install the SAS kernel package. The package has a dependency on the SASPy 172 | package. The SASPy package is available from https://github.com/sassoftware/saspy. 173 | If the ``pip`` command in your path does not map to Python3, then use ``pip3`` 174 | instead. 175 | :: 176 | 177 | pip install sas_kernel 178 | 179 | #. Verify that the package is installed. 180 | :: 181 | 182 | jupyter kernelspec list 183 | 184 | Your output should look similar to the following: 185 | :: 186 | 187 | Available kernels: 188 | python3 C:\Users\sas\AppData\Local\Continuum\Anaconda3\lib\site-packages\ipykernel\resources 189 | sas C:\ProgramData\jupyter\kernels\sas 190 | 191 | #. Configure the SAS executable for your system. 192 | 193 | The connection of the Jupyter notebook to SAS is made by the SASPy Python package (which was installed as a dependency 194 | for you). 195 | 196 | 197 | Use the `SASPy configuration documentation`_ to complete you set up. 198 | 199 | .. _SASPy configuration documentation: https://sassoftware.github.io/saspy/configuration.html 200 | 201 | * SASPy documentation: https://sassoftware.github.io/saspy/index.html 202 | * More information about SASPy: https://github.com/sassoftware/saspy 203 | 204 | 205 | 206 | ***************** 207 | OSX (Mac) install 208 | ***************** 209 | 210 | #. Go to https://www.continuum.io/downloads and install 211 | Anaconda Python (make sure you get Python3.X). If you install 212 | Anaconda without Administrator privileges, then other users 213 | on the system will not be able to access the SAS kernel. Consider the 214 | following: 215 | 216 | * Install in the default location unless you have a good reason to change it. 217 | Using the default location simplifies administration. 218 | 219 | * One installation prompt is to make Python accessible for just your account 220 | or for all users. Select the best response for your situation. 221 | 222 | * Another installation prompt is to add the path to your environment. SAS 223 | recommends that you answer 'yes' to the prompt so that you get the 224 | executables in your path automatically. Adding the path your environment 225 | simplifies starting Python and Jupyter. 226 | 227 | #. Install the SAS kernel package. The package has a dependency on the SASPy 228 | package. The SASPy package is available from https://github.com/sassoftware/saspy. 229 | If the ``pip`` command in your path does not map to Python3, then use ``pip3`` 230 | instead. 231 | :: 232 | 233 | pip install sas_kernel 234 | 235 | #. Verify that the package is installed. 236 | :: 237 | 238 | jupyter kernelspec list 239 | 240 | Your output should look similar to the following: 241 | :: 242 | 243 | Available kernels: 244 | python3 /Users/sas/anaconda3/lib/python3.5/site-packages/ipykernel/resources 245 | sas /usr/local/share/jupyter/kernels/sas 246 | 247 | 248 | #. Configure the SAS executable for your system. 249 | 250 | The connection of the Jupyter notebook to SAS is made by the SASPy Python package (which was installed as a dependency 251 | for you). 252 | 253 | 254 | Use the `SASPy configuration documentation`_ to complete you set up. 255 | 256 | .. _SASPy configuration documentation: https://sassoftware.github.io/saspy/configuration.html 257 | 258 | * SASPy documentation: https://sassoftware.github.io/saspy/index.html 259 | * More information about SASPy: https://github.com/sassoftware/saspy 260 | 261 | 262 | =========================== 263 | Installing SAS NBextensions 264 | =========================== 265 | 266 | ******************** 267 | Installing from PyPi 268 | ******************** 269 | 270 | With the release of Jupyter 4.2 (SAS kernel package version 1.2) the 271 | installation and enabling of nbextensions is improved. To install and 272 | enable the showSASLog extension use the following commands. 273 | 274 | :: 275 | 276 | jupyter nbextension install --py sas_kernel.showSASLog 277 | jupyter nbextension enable sas_kernel.showSASLog --py 278 | 279 | To install and enable the theme extension use the following commands. 280 | 281 | :: 282 | 283 | jupyter nbextension install --py sas_kernel.theme 284 | jupyter nbextension enable sas_kernel.theme --py 285 | 286 | To verify the nbextensions that you installed use the following commands. 287 | 288 | :: 289 | 290 | jupyter nbextension list 291 | 292 | If the extensions are correctly installed, you will see output similar to 293 | the following: 294 | 295 | :: 296 | 297 | Known nbextensions: 298 | config dir: /root/.jupyter/nbconfig 299 | notebook section 300 | showSASLog/main enabled 301 | - Validating: OK 302 | theme/theme_selector enabled 303 | - Validating: OK 304 | 305 | *********************************** 306 | Installing from a cloned repository 307 | *********************************** 308 | 309 | The cloned repository has a directory for each nbextension within the 310 | file structure as shown below: 311 | 312 | :: 313 | 314 | sas_kernel 315 | | 316 | +-- showSASLog 317 | +-- theme 318 | 319 | You can install the extensions from the command line. To install an extension 320 | system-wide, use the following command with Superuser privileges (root or 321 | sudo). The following command assumes that you are in the nbextensions 322 | directory. Adjust the path if you are not. 323 | 324 | :: 325 | 326 | jupyter nbextension install ./showSASLog 327 | 328 | Your output should look similar to the following (installed with Superuser 329 | privileges): 330 | 331 | :: 332 | 333 | copying showSASLog/main.js -> /usr/local/share/jupyter/nbextensions/main.js 334 | 335 | To install for your user account only, use the following command. Again, 336 | the sample command assumes that you are in the nbextensions directory. Adjust 337 | the path if you are not. 338 | 339 | :: 340 | 341 | jupyter nbextension install ./showSASLog --user 342 | 343 | Your output should look similar to the following (installed for your user 344 | account only): 345 | 346 | :: 347 | 348 | copying showSASLog/main.js -> /home/sas/.local/share/jupyter/nbextensions/showSASLog/main.js 349 | 350 | Then enable the notebook extension with the following command. 351 | 352 | :: 353 | 354 | jupyter nbextension enable showSASLog 355 | 356 | To disable the extension, you can run the following command. 357 | 358 | :: 359 | 360 | jupyter nbextension disable showSASLog 361 | 362 | Example 363 | ======= 364 | 365 | There is a `notebook`_ that walks through the steps to install and 366 | enable the extensions. 367 | 368 | .. _notebook: https://github.com/sassoftware/sas_kernel/blob/master/notebook/loadSASExtensions.ipynb 369 | -------------------------------------------------------------------------------- /sas_kernel/doc/source/license.rst: -------------------------------------------------------------------------------- 1 | 2 | .. Copyright SAS Institute 3 | 4 | .. _license: 5 | 6 | :tocdepth: 2 7 | 8 | ******* 9 | License 10 | ******* 11 | 12 | Apache 2.0 13 | ========== 14 | 15 | :Date: January 2004 16 | :URL: http://www.apache.org/licenses/ 17 | 18 | ------------------------------------------------------------ 19 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 20 | ------------------------------------------------------------ 21 | 22 | 1. Definitions. 23 | --------------- 24 | 25 | **"License"** shall mean the terms and conditions for use, reproduction, and 26 | distribution as defined by Sections 1 through 9 of this document. 27 | 28 | **"Licensor"** shall mean the copyright owner or entity authorized by the 29 | copyright owner that is granting the License. 30 | 31 | **"Legal Entity"** shall mean the union of the acting entity and all other 32 | entities that control, are controlled by, or are under common control with that 33 | entity. For the purposes of this definition, "control" means *(i)* the power, 34 | direct or indirect, to cause the direction or management of such entity, 35 | whether by contract or otherwise, or *(ii)* ownership of fifty percent (50%) or 36 | more of the outstanding shares, or *(iii)* beneficial ownership of such entity. 37 | 38 | **"You"** (or **"Your"**) shall mean an individual or Legal Entity exercising 39 | permissions granted by this License. 40 | 41 | **"Source"** form shall mean the preferred form for making modifications, 42 | including but not limited to software source code, documentation source, and 43 | configuration files. 44 | 45 | **"Object"** form shall mean any form resulting from mechanical transformation 46 | or translation of a Source form, including but not limited to compiled object 47 | code, generated documentation, and conversions to other media types. 48 | 49 | **"Work"** shall mean the work of authorship, whether in Source or Object form, 50 | made available under the License, as indicated by a copyright notice that is 51 | included in or attached to the work (an example is provided in the Appendix 52 | below). 53 | 54 | **"Derivative Works"** shall mean any work, whether in Source or Object form, 55 | that is based on (or derived from) the Work and for which the editorial 56 | revisions, annotations, elaborations, or other modifications represent, as a 57 | whole, an original work of authorship. For the purposes of this License, 58 | Derivative Works shall not include works that remain separable from, or merely 59 | link (or bind by name) to the interfaces of, the Work and Derivative Works 60 | thereof. 61 | 62 | **"Contribution"** shall mean any work of authorship, including the original 63 | version of the Work and any modifications or additions to that Work or 64 | Derivative Works thereof, that is intentionally submitted to Licensor for 65 | inclusion in the Work by the copyright owner or by an individual or Legal 66 | Entity authorized to submit on behalf of the copyright owner. For the purposes 67 | of this definition, "submitted" means any form of electronic, verbal, or 68 | written communication sent to the Licensor or its representatives, including 69 | but not limited to communication on electronic mailing lists, source code 70 | control systems, and issue tracking systems that are managed by, or on behalf 71 | of, the Licensor for the purpose of discussing and improving the Work, but 72 | excluding communication that is conspicuously marked or otherwise designated in 73 | writing by the copyright owner as "Not a Contribution." 74 | 75 | **"Contributor"** shall mean Licensor and any individual or Legal Entity on 76 | behalf of whom a Contribution has been received by Licensor and subsequently 77 | incorporated within the Work. 78 | 79 | 2. Grant of Copyright License. 80 | ------------------------------ 81 | 82 | Subject to the terms and conditions of this License, each Contributor hereby 83 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 84 | irrevocable copyright license to reproduce, prepare Derivative Works of, 85 | publicly display, publicly perform, sublicense, and distribute the Work and 86 | such Derivative Works in Source or Object form. 87 | 88 | 3. Grant of Patent License. 89 | --------------------------- 90 | 91 | Subject to the terms and conditions of this License, each Contributor hereby 92 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 93 | irrevocable (except as stated in this section) patent license to make, have 94 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 95 | such license applies only to those patent claims licensable by such Contributor 96 | that are necessarily infringed by their Contribution(s) alone or by combination 97 | of their Contribution(s) with the Work to which such Contribution(s) was 98 | submitted. If You institute patent litigation against any entity (including a 99 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 100 | Contribution incorporated within the Work constitutes direct or contributory 101 | patent infringement, then any patent licenses granted to You under this License 102 | for that Work shall terminate as of the date such litigation is filed. 103 | 104 | 4. Redistribution. 105 | ------------------ 106 | 107 | You may reproduce and distribute copies of the Work or Derivative Works thereof 108 | in any medium, with or without modifications, and in Source or Object form, 109 | provided that You meet the following conditions: 110 | 111 | - You must give any other recipients of the Work or Derivative Works a copy of 112 | this License; and 113 | 114 | - You must cause any modified files to carry prominent notices stating that You 115 | changed the files; and 116 | 117 | - You must retain, in the Source form of any Derivative Works that You 118 | distribute, all copyright, patent, trademark, and attribution notices from 119 | the Source form of the Work, excluding those notices that do not pertain to 120 | any part of the Derivative Works; and 121 | 122 | - If the Work includes a ``"NOTICE"`` text file as part of its distribution, 123 | then any Derivative Works that You distribute must include a readable copy of 124 | the attribution notices contained within such ``NOTICE`` file, excluding 125 | those notices that do not pertain to any part of the Derivative Works, in at 126 | least one of the following places: within a ``NOTICE`` text file distributed 127 | as part of the Derivative Works; within the Source form or documentation, if 128 | provided along with the Derivative Works; or, within a display generated by 129 | the Derivative Works, if and wherever such third-party notices normally 130 | appear. The contents of the ``NOTICE`` file are for informational purposes 131 | only and do not modify the License. You may add Your own attribution notices 132 | within Derivative Works that You distribute, alongside or as an addendum to 133 | the ``NOTICE`` text from the Work, provided that such additional attribution 134 | notices cannot be construed as modifying the License. You may add Your own 135 | copyright statement to Your modifications and may provide additional or 136 | different license terms and conditions for use, reproduction, or distribution 137 | of Your modifications, or for any such Derivative Works as a whole, provided 138 | Your use, reproduction, and distribution of the Work otherwise complies with 139 | the conditions stated in this License. 140 | 141 | 5. Submission of Contributions. 142 | ------------------------------- 143 | 144 | Unless You explicitly state otherwise, any Contribution intentionally submitted 145 | for inclusion in the Work by You to the Licensor shall be under the terms and 146 | conditions of this License, without any additional terms or conditions. 147 | Notwithstanding the above, nothing herein shall supersede or modify the terms 148 | of any separate license agreement you may have executed with Licensor regarding 149 | such Contributions. 150 | 151 | 6. Trademarks. 152 | -------------- 153 | 154 | This License does not grant permission to use the trade names, trademarks, 155 | service marks, or product names of the Licensor, except as required for 156 | reasonable and customary use in describing the origin of the Work and 157 | reproducing the content of the ``NOTICE`` file. 158 | 159 | 7. Disclaimer of Warranty. 160 | -------------------------- 161 | 162 | Unless required by applicable law or agreed to in writing, Licensor provides 163 | the Work (and each Contributor provides its Contributions) on an **"AS IS" 164 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND**, either express or 165 | implied, including, without limitation, any warranties or conditions of 166 | **TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR 167 | PURPOSE**. You are solely responsible for determining the appropriateness of 168 | using or redistributing the Work and assume any risks associated with Your 169 | exercise of permissions under this License. 170 | 171 | 8. Limitation of Liability. 172 | --------------------------- 173 | 174 | In no event and under no legal theory, whether in tort (including negligence), 175 | contract, or otherwise, unless required by applicable law (such as deliberate 176 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 177 | liable to You for damages, including any direct, indirect, special, incidental, 178 | or consequential damages of any character arising as a result of this License 179 | or out of the use or inability to use the Work (including but not limited to 180 | damages for loss of goodwill, work stoppage, computer failure or malfunction, 181 | or any and all other commercial damages or losses), even if such Contributor 182 | has been advised of the possibility of such damages. 183 | 184 | 9. Accepting Warranty or Additional Liability. 185 | ---------------------------------------------- 186 | 187 | While redistributing the Work or Derivative Works thereof, You may choose to 188 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 189 | other liability obligations and/or rights consistent with this License. 190 | However, in accepting such obligations, You may act only on Your own behalf and 191 | on Your sole responsibility, not on behalf of any other Contributor, and only 192 | if You agree to indemnify, defend, and hold each Contributor harmless for any 193 | liability incurred by, or claims asserted against, such Contributor by reason 194 | of your accepting any such warranty or additional liability. 195 | 196 | **END OF TERMS AND CONDITIONS** 197 | 198 | APPENDIX: How to apply the Apache License to your work 199 | ------------------------------------------------------ 200 | 201 | To apply the Apache License to your work, attach the following boilerplate 202 | notice, with the fields enclosed by brackets "[]" replaced with your own 203 | identifying information. (Don't include the brackets!) The text should be 204 | enclosed in the appropriate comment syntax for the file format. We also 205 | recommend that a file or class name and description of purpose be included on 206 | the same "printed page" as the copyright notice for easier identification within 207 | third-party archives. :: 208 | 209 | Copyright [yyyy] [name of copyright owner] 210 | 211 | Licensed under the Apache License, Version 2.0 (the "License"); 212 | you may not use this file except in compliance with the License. 213 | You may obtain a copy of the License at 214 | 215 | http://www.apache.org/licenses/LICENSE-2.0 216 | 217 | Unless required by applicable law or agreed to in writing, software 218 | distributed under the License is distributed on an "AS IS" BASIS, 219 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 220 | See the License for the specific language governing permissions and 221 | limitations under the License. 222 | -------------------------------------------------------------------------------- /sas_kernel/doc/source/overview.rst: -------------------------------------------------------------------------------- 1 | ###################### 2 | Overview of SAS kernel 3 | ###################### 4 | .. I used http://documentation-style-guide-sphinx.readthedocs.io/en/latest/style-guide.html 5 | 6 | 7 | ************* 8 | What is this? 9 | ************* 10 | 11 | A SAS kernel for `Jupyter Notebooks `_. Jupyter Notebooks 12 | are capable of running programs in a variety of programming languages and it is 13 | the kernel that enables this ability. The SAS kernel enables Jupyter Notebook to 14 | provide the following programming experience: 15 | 16 | * syntax highlighting for SAS programming statements 17 | * store the input and output from an interactive SAS session 18 | 19 | After installing the SAS kernel, you can use a notebook and a SAS installation to 20 | write, document, and submit SAS programming statements. 21 | 22 | 23 | ************ 24 | Dependencies 25 | ************ 26 | 27 | - Python3.X or higher. 28 | - Jupyter 29 | - SAS 9.4 or higher. SAS Viya 3.1 or higher is also supported. 30 | 31 | Previous release of the SAS kernel supported connecting to SAS on Linux only. For this release, 32 | you can connect to SAS on any platform that is supported for the specified SAS releases. 33 | 34 | Jupyter has a number of dependencies. See the subsections for steps on installing Jupyter on 35 | your system. 36 | 37 | 38 | ************** 39 | Jupyter magics 40 | ************** 41 | The %%prompt4var magic is written specifically for the SAS kernel. The purpose of 42 | the magic is to prompt for sensitive information such as a password and store 43 | the value in a SAS macro variable. 44 | 45 | Seasoned SAS programmers might notice that Python magics begin with a percent 46 | sign (%) and that SAS macro variables also begin with a percent sign. To ensure 47 | that magics are interpreted by Python, they must be specified in the first line 48 | of a notebook cell. Otherwise, the magic (%xxxxx) is submitted to SAS. 49 | 50 | If you need to run a SAS macro as the first statement, then insert a blank line 51 | as the first line in the notebook cell and the macro on the second line. The 52 | blank line prevents Python from intrepreting the macro variable as a magic. 53 | 54 | 55 | ************ 56 | NBExtensions 57 | ************ 58 | There are a few NBExtensions to make working with notebooks more productive and pleasant. 59 | These are largely the result of pain points and gotchas. These include: 60 | 61 | SAS Log 62 | This extension shows the SAS log for the last executed cell or the entire log 63 | since the last restart of the notebook. 64 | 65 | Themes 66 | This extension enables you to change the color scheme for your code to match 67 | the traditional SAS theme. 68 | 69 | You can install the extensions after you install the SAS kernel. Installation information 70 | is provided in this documentation. The source code for the extensions can be found at 71 | https://github.com/sassoftware/sas_kernel/tree/master/sas_kernel/nbextensions. 72 | 73 | 74 | **************************************** 75 | Integration with other notebook software 76 | **************************************** 77 | 78 | JupyterHub 79 | ========== 80 | 81 | The SAS kernel can be used with JupyterHub. For more information, see 82 | https://jupyterhub.readthedocs.org. 83 | 84 | NBGrader 85 | ======== 86 | 87 | NBGrader is a system for assigning and grading notebooks and extends 88 | Jupyter Notebook. For more information, see http://nbgrader.readthedocs.org. 89 | 90 | This forked repo (https://github.com/jld23/nbgrader) includes 91 | a number of contributions that are used for 92 | teaching SAS programming in a classroom setting. 93 | 94 | 95 | *** 96 | FAQ 97 | *** 98 | 99 | - Do I need to buy SAS to use this kernel? 100 | 101 | The SAS kernel is simply a program that enables Jupyter to communicate 102 | with SAS. As such, if SAS is not installed, then this kernel is not helpful. 103 | For information about purchasing SAS, see 104 | http://www.sas.com/en_us/software/how-to-buy.html. 105 | 106 | - How does Jupyter communicate with SAS? 107 | 108 | Behind a Jupyter Notebook is a Python session. The Python session 109 | submits code to SAS and receives responses through socket I/O communication. 110 | The submit and receive strategy leverages the stdin, stdout, and stderr 111 | capabilities that have been supported in SAS for a long time. 112 | 113 | - If stdin, stdout, and stderr have been supported for so long why do I 114 | need to have SAS 9.4 or newer? 115 | 116 | First, SAS 9.4 was released in July 2013, so it is not a bleeding edge 117 | requirement. The reason for the prerequisite is that SAS 9.4 introduced 118 | support for creating of HTML5 documents. The SAS kernel relies on the 119 | HTML5 output so that it can render attractive tables and graphs automagically. 120 | 121 | For information about SAS Viya, see http://www.sas.com/viya. 122 | 123 | - How can I see my SAS log, I only see the listing output? 124 | 125 | SAS is different from many other programming languages in that it has 126 | two useful information streams, the log (which details the technical 127 | details of what happened and how long it took) and the lst (which 128 | includes the tables and graphics from the analysis). The SAS kernel 129 | attempts to show you what we *think* you want. Here are the rules: 130 | 131 | 132 | +-------------------------------+-------+---------------------------------------------------------------------+-----------------------------------------------------------------------------+ 133 | | LOG | LST | DISPLAYED | NOTES | 134 | +===============================+=======+=====================================================================+=============================================================================+ 135 | | Yes | No | LOG | This happens when you run DATA step or a PROC with the ``noprint`` option. | 136 | +-------------------------------+-------+---------------------------------------------------------------------+-----------------------------------------------------------------------------+ 137 | | Yes | Yes | LST | --- | 138 | +-------------------------------+-------+---------------------------------------------------------------------+-----------------------------------------------------------------------------+ 139 | | Yes (with ERROR message(s)) | Yes | ERROR messages with context from the log, then the listing output. | --- | 140 | +-------------------------------+-------+---------------------------------------------------------------------+-----------------------------------------------------------------------------+ 141 | | Yes (with ERROR message(s)) | No | LOG | --- | 142 | +-------------------------------+-------+---------------------------------------------------------------------+-----------------------------------------------------------------------------+ 143 | 144 | If you want to see the log but it was not displayed, you can use 145 | the SAS log NBExtension. The extension shows the log for the last 146 | executed cell or the entire log since the last (re)start of the notebook. 147 | 148 | - Will this leave a bunch of SAS sessions hanging around? 149 | 150 | A SAS session is started for each notebook that you have open. For example, 151 | if you open 5 notebooks, you start 5 SAS sessions. Those sessions remain 152 | active as long as the notebook is running. If you shut down your notebook, 153 | the associated SAS session terminates. In JupyterHub, there are configuration 154 | options to shut down inactive sessions and the SAS kernel complies 155 | with those directives. 156 | 157 | - I restarted my SAS kernel and now my Work library is now empty. What 158 | happened? 159 | 160 | When you restart the kernel in a notebook, you terminate the SAS session 161 | and start a new one. All of the temporary artifacts, such as data sets in 162 | the Work library, assigned librefs, filerefs, Work macros, and so on, 163 | are destroyed. 164 | -------------------------------------------------------------------------------- /sas_kernel/install.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright SAS Institute 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the License); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """Script that installs the kernel""" 17 | 18 | import os 19 | import sys 20 | import json 21 | import getopt 22 | from shutil import copyfile 23 | from .data import _dataRoot 24 | 25 | try: 26 | from jupyter_client.kernelspec import install_kernel_spec 27 | except ImportError: 28 | try: 29 | from IPython.kernel.kernelspec import install_kernel_spec 30 | except ImportError: 31 | print("Please install either Jupyter to IPython before continuing") 32 | from IPython.utils.tempdir import TemporaryDirectory 33 | 34 | kernel_json = { 35 | "argv": [sys.executable, 36 | "-m", "sas_kernel", "-f", "{connection_file}"], 37 | "display_name": "SAS", 38 | "codemirror_mode": "sas", 39 | "language": "sas", 40 | "name": "sas", 41 | } 42 | 43 | 44 | def install_my_kernel_spec(user=True, prefix=None): 45 | user = '--user' in sys.argv or not _is_root() 46 | with TemporaryDirectory() as td: 47 | os.chmod(td, 0o755) # Starts off as 700, not user readable 48 | 49 | with open(os.path.join(td, 'kernel.json'), 'w') as f: 50 | json.dump(kernel_json, f, sort_keys=True) 51 | 52 | copyfile(os.path.join(_dataRoot, 'logo-64x64.png'), 53 | os.path.join(td, 'logo-64x64.png')) 54 | 55 | try: 56 | install_kernel_spec(td, kernel_json['name'], user=user, 57 | replace=True, prefix=prefix) 58 | except OSError: 59 | install_kernel_spec(td, kernel_json['name'], user=not user, 60 | replace=True, prefix=prefix) 61 | 62 | def main(argv=()): 63 | prefix = None 64 | user = not _is_root() 65 | 66 | opts, _ = getopt.getopt(argv[1:], '', ['user', 'prefix=']) 67 | for k, v in opts: 68 | if k == '--user': 69 | user = True 70 | elif k == '--prefix': 71 | prefix = v 72 | user = False 73 | 74 | install_my_kernel_spec(user=user, prefix=prefix) 75 | 76 | 77 | def _is_root(): 78 | try: 79 | return os.geteuid() == 0 80 | except AttributeError: 81 | return False # assume not an admin on non-Unix platforms 82 | 83 | 84 | if __name__ == '__main__': 85 | main(argv=sys.argv) 86 | -------------------------------------------------------------------------------- /sas_kernel/kernel.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright SAS Institute 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the License); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | import os 17 | import sys 18 | import re 19 | import json 20 | import types 21 | import importlib.machinery 22 | # Create LOGGER 23 | import logging 24 | import saspy 25 | 26 | from typing import Tuple, Union 27 | from IPython.display import HTML 28 | from metakernel import MetaKernel 29 | from .version import __version__ 30 | 31 | 32 | # create a LOGGER to output messages to the Jupyter CONSOLE 33 | LOGGER = logging.getLogger(__name__) 34 | LOGGER.setLevel(logging.WARN) 35 | CONSOLE = logging.StreamHandler() 36 | CONSOLE.setFormatter(logging.Formatter('%(name)-12s: %(message)s')) 37 | LOGGER.addHandler(CONSOLE) 38 | 39 | LOGGER.debug("sanity check") 40 | 41 | 42 | class SASKernel(MetaKernel): 43 | """ 44 | SAS Kernel for Jupyter implementation. This module relies on SASPy 45 | """ 46 | implementation = 'sas_kernel' 47 | implementation_version = __version__ 48 | language = 'sas' 49 | language_version = '9.4+', 50 | banner = "SAS Kernel" 51 | language_info = {'name': 'sas', 52 | 'mimetype': 'text/x-sas', 53 | "codemirror_mode": "sas", 54 | 'file_extension': '.sas' 55 | } 56 | 57 | def __init__(self, **kwargs): 58 | with open(os.path.dirname(os.path.realpath(__file__)) + \ 59 | '/data/' + 'sasproclist.json') as proclist: 60 | self.proclist = json.load(proclist) 61 | with open(os.path.dirname(os.path.realpath(__file__)) + \ 62 | '/data/' + 'sasgrammardictionary.json') as compglo: 63 | self.compglo = json.load(compglo) 64 | self.strproclist = '\n'.join(str(x) for x in self.proclist) 65 | self.promptDict = {} 66 | MetaKernel.__init__(self, **kwargs) 67 | self.lst_len = 0 68 | self.mva = None 69 | self.cachedlog = None 70 | self._allow_stdin = False 71 | 72 | def do_apply(self, content, bufs, msg_id, reply_metadata): 73 | pass 74 | 75 | def do_clear(self): 76 | pass 77 | 78 | def get_usage(self): 79 | return "This is the SAS kernel." 80 | 81 | def _get_config_names(self): 82 | """ 83 | get the config file used by SASPy 84 | """ 85 | loader = importlib.machinery.SourceFileLoader('foo', saspy.SAScfg) 86 | cfg = types.ModuleType(loader.name) 87 | loader.exec_module(cfg) 88 | return cfg.SAS_config_names 89 | 90 | def _start_sas(self, **kwargs): 91 | session_params = kwargs 92 | if session_params is not None: 93 | for _, v in session_params.items(): 94 | assert isinstance(v, str) 95 | try: 96 | self.mva = saspy.SASsession(kernel=self, **kwargs) 97 | except KeyError: 98 | self.mva = None 99 | except OSError:#socket.gaierror 100 | msg = """Failed to connect to SAS! 101 | Please check your connection configuration here:{0} 102 | Here are the valid configurations:{1} 103 | You can load the configuration file into a Jupyter Lab cell using this command: 104 | %load {0} 105 | If the URL/Path are correct the issue is likely your username and/or password 106 | """.format(saspy.list_configs()[0], ', '.join(self._get_config_names())) 107 | self.Error_display(msg) 108 | self.mva = None 109 | except Exception: 110 | # self.Error_display('\n'.join(list(sys.exc_info()))) 111 | self.Error_display(str([l for l in sys.exc_info()])) 112 | # return 113 | 114 | 115 | def _colorize_log(self, log: str) -> str: 116 | """ 117 | takes a SAS log (str) and then looks for errors. 118 | Returns a tuple of error count, list of error messages 119 | """ 120 | regex_note = r"(?m)(^NOTE.*((\n|\t|\n\t)[ ]([^WEN].*)(.*))*)" 121 | regex_warn = r"(?m)(^WARNING.*((\n|\t|\n\t)[ ]([^WEN].*)(.*))*)" 122 | regex_error = r"(?m)(^ERROR.*((\n|\t|\n\t)[ ]([^WEN].*)(.*))*)" 123 | 124 | sub_note = "\x1b[38;5;21m\\1\x1b[0m" 125 | sub_warn = "\x1b[38;5;2m\\1\x1b[0m" 126 | sub_error = "\x1B[1m\x1b[38;5;9m\\1\x1b[0m\x1b[0m" 127 | color_pattern = [ 128 | (regex_error, sub_error), 129 | (regex_note, sub_note), 130 | (regex_warn, sub_warn) 131 | ] 132 | colored_log = log 133 | for pat, sub in color_pattern: 134 | colored_log = re.sub(pat, sub, colored_log) 135 | 136 | return colored_log 137 | 138 | def _is_error_log(self, log: str) -> Tuple: 139 | """ 140 | takes a SAS log (str) and then looks for errors. 141 | Returns a tuple of error count, list of error messages 142 | """ 143 | lines = re.split(r'[\n]\s*', log) 144 | error_count = 0 145 | error_log_msg_list = [] 146 | error_log_line_list = [] 147 | for index, line in enumerate(lines): 148 | # LOGGER.debug("line:{}".format(line)) 149 | if line.startswith('ERROR'): 150 | error_count += 1 151 | error_log_msg_list.append(line) 152 | error_log_line_list.append(index) 153 | return (error_count, error_log_msg_list, error_log_line_list) 154 | 155 | def _which_display(self, log: str, output: str = '') -> str: 156 | """ 157 | Determines if the log or lst should be returned as the 158 | results for the cell based on parsing the log 159 | looking for errors and the presence of lst output. 160 | 161 | :param log: str log from code submission 162 | :param output: None or str lst output if there was any 163 | :return: The correct results based on log and lst 164 | :rtype: str 165 | """ 166 | error_count, msg_list, error_line_list = self._is_error_log(log) 167 | 168 | # no error and LST output 169 | if error_count == 0 and len(output) > self.lst_len: 170 | return self.Display(HTML(output)) 171 | 172 | elif error_count > 0 and len(output) > self.lst_len: # errors and LST 173 | # filter log to lines around first error 174 | # by default get 5 lines on each side of the first Error message. 175 | # to change that modify the values in {} below 176 | regex_around_error = r"(.*)(.*\n){6}^ERROR(.*\n){6}" 177 | 178 | # Extract the first match +/- 5 lines 179 | e_log = re.search(regex_around_error, log, re.MULTILINE).group() 180 | assert error_count == len( 181 | error_line_list), "Error count and count of line number don't match" 182 | return self.Error_display(msg_list[0], 183 | print(self._colorize_log(e_log)), 184 | HTML(output)) 185 | 186 | # for everything else return the log 187 | return self.Print(self._colorize_log(log)) 188 | 189 | def do_execute_direct(self, code: str, silent: bool = False) -> Union[str, dict]: 190 | """ 191 | This is the main method that takes code from the Jupyter cell 192 | and submits it to the SAS server. 193 | 194 | :param code: code from the cell 195 | :param silent: 196 | :return: str with either the log or list 197 | """ 198 | if not code.strip(): 199 | return {'status': 'ok', 'execution_count': self.execution_count, 200 | 'payload': [], 'user_expressions': {}} 201 | 202 | # If no mva session start a session 203 | if self.mva is None: 204 | self._allow_stdin = True 205 | self._start_sas() 206 | 207 | # This block uses special strings submitted by the Jupyter notebook extensions 208 | if not code.startswith('showSASLog_11092015') and \ 209 | not code.startswith("CompleteshowSASLog_11092015"): 210 | if code.startswith("/*SASKernelTest*/"): 211 | res = self.mva.submit(code, "text") 212 | else: 213 | res = self.mva.submit(code, prompt=self.promptDict) 214 | self.promptDict = {} 215 | if res['LOG'].find("SAS process has terminated unexpectedly") > -1: 216 | print(res['LOG'], '\n' "Restarting SAS session on your behalf") 217 | self.do_shutdown(True) 218 | return res['LOG'] 219 | 220 | # store the log for display in the showSASLog nbextension 221 | self.cachedlog = self._colorize_log(res['LOG']) 222 | 223 | # Parse the log to check for errors 224 | error_count, error_log_msg, _ = self._is_error_log(res['LOG']) 225 | 226 | if error_count > 0 and len(res['LST']) <= self.lst_len: 227 | return self.Error(error_log_msg[0], print(self._colorize_log(res['LOG']))) 228 | 229 | return self._which_display(res['LOG'], res['LST']) 230 | 231 | elif code.startswith("CompleteshowSASLog_11092015") and \ 232 | not code.startswith('showSASLog_11092015'): 233 | return self.Print(self._colorize_log(self.mva.saslog())) 234 | else: 235 | return self.Print(self._colorize_log(self.cachedlog)) 236 | 237 | def get_completions(self, info): 238 | """ 239 | Get completions from kernel for procs and statements. 240 | """ 241 | if info['line_num'] > 1: 242 | relstart = info['column'] - (info['help_pos'] - info['start']) 243 | else: 244 | relstart = info['start'] 245 | seg = info['line'][:relstart] 246 | try: 247 | if relstart > 0 and re.match('(?i)proc', seg.rsplit(None, 1)[-1]): 248 | potentials = re.findall( 249 | '(?i)^' + info['obj'] + '.*', self.strproclist, re.MULTILINE) 250 | return potentials 251 | except: 252 | pass 253 | 254 | lastproc = info['code'].lower()[:info['help_pos']].rfind('proc') 255 | lastdata = info['code'].lower()[:info['help_pos']].rfind('data ') 256 | proc = False 257 | data = False 258 | if lastproc + lastdata == -2: 259 | pass 260 | else: 261 | if lastproc > lastdata: 262 | proc = True 263 | else: 264 | data = True 265 | 266 | if proc: 267 | # we are not in data section should see if proc option or statement 268 | lastsemi = info['code'].rfind(';') 269 | mykey = 's' 270 | if lastproc > lastsemi: 271 | mykey = 'p' 272 | procer = re.search(r'(?i)proc\s\w+', info['code'][lastproc:]) 273 | method = procer.group(0).split(' ')[-1].upper() + mykey 274 | mylist = self.compglo[method][0] 275 | potentials = re.findall( 276 | '(?i)' + info['obj'] + '.+', '\n'.join(str(x) for x in mylist), re.MULTILINE) 277 | return potentials 278 | elif data: 279 | # we are in statements (probably if there is no data) 280 | # assuming we are in the middle of the code 281 | 282 | lastsemi = info['code'].rfind(';') 283 | mykey = 's' 284 | if lastproc > lastsemi: 285 | mykey = 'p' 286 | mylist = self.compglo['DATA' + mykey][0] 287 | potentials = re.findall( 288 | '(?i)^' + info['obj'] + '.*', '\n'.join(str(x) for x in mylist), re.MULTILINE) 289 | return potentials 290 | else: 291 | potentials = [''] 292 | return potentials 293 | 294 | @staticmethod 295 | def _get_right_list(s): 296 | proc_opt = re.search( 297 | r"proc\s(\w+).*?[^;]\Z", s, re.IGNORECASE | re.MULTILINE) 298 | proc_stmt = re.search(r"\s*proc\s*(\w+).*;.*\Z", 299 | s, re.IGNORECASE | re.MULTILINE) 300 | data_opt = re.search( 301 | r"\s*data\s*[^=].*[^;]?.*$", s, re.IGNORECASE | re.MULTILINE) 302 | data_stmt = re.search( 303 | r"\s*data\s*[^=].*[^;]?.*$", s, re.IGNORECASE | re.MULTILINE) 304 | print(s) 305 | if proc_opt: 306 | return proc_opt.group(1).upper() + 'p' 307 | elif proc_stmt: 308 | return proc_stmt.group(1).upper() + 's' 309 | elif data_opt: 310 | return 'DATA' + 'p' 311 | elif data_stmt: 312 | return 'DATA' + 's' 313 | else: 314 | return None 315 | 316 | def initialize_debug(self, code): 317 | """SAS does not have formal debug tools from this interface""" 318 | print(code) 319 | return None 320 | 321 | def do_shutdown(self, restart): 322 | """ 323 | Shut down the app gracefully, saving history. 324 | """ 325 | print("in shutdown function") 326 | if self.hist_file: 327 | with open(self.hist_file, 'wb') as fid: 328 | data = '\n'.join(self.hist_cache[-self.max_hist_cache:]) 329 | fid.write(data.encode('utf-8')) 330 | if self.mva: 331 | self.mva._endsas() 332 | self.mva = None 333 | if restart: 334 | self.Print("Restarting kernel...") 335 | self.reload_magics() 336 | self.restart_kernel() 337 | self.Print("Done!") 338 | return {'status': 'ok', 'restart': restart} 339 | -------------------------------------------------------------------------------- /sas_kernel/magics/README.md: -------------------------------------------------------------------------------- 1 | # SAS Kernel Magics 2 | Jupyter magics are special functions that perform tasks before code is sent to the kernel 3 | To see the list of magic you can use the `%lsmagic` 4 | 5 | Magics must be at the the first line of the cell or else they will be submitted to the kernel for execution 6 | This presents a problem for SAS since SAS Macro (R) uses `%` to denote a macro variable. Currently if you have a SAS Macro in the first line it will try and interpret it as a magic. Entering a blank line will resolve the issue 7 | 8 | ## prompt4var 9 | prompt4var includes both a line and cell magics. The general function of the magic is to prompt the user for values to create macro variables in the SAS session associated with that notebook. 10 | ### Line Magic 11 | 12 | #### Examples 13 | 1. Create macro variables without code. The code below will prompt each time the cell is executed for `libpath` and `file1`. There is no associated code and the those macro variables will exist for remainder of session (or until kernel is restarted) 14 | ``` 15 | %prompt4var libpath file1 16 | ``` 17 | 18 | 1. Line prompt with code included 19 | 20 | ``` 21 | %prompt4var libpath file1 22 | filename myfile "~&file1."; 23 | libname data "&libpath"; 24 | ``` 25 | ### Cell Magic 26 | The cell magic prompts users for variables that are intended to be private -- passwords and such. The macro variables will be deleted from the when the cell finishes processing. Libnames assigned will still be active but the password will not be stored anywhere. 27 | 28 | #### Examples 29 | 1. Password protecting a data set 30 | 31 | ``` 32 | %%prompt4var alter read 33 | data work.cars(alter="&alter" read="&read"); 34 | set sashelp.cars; 35 | id = _n_; 36 | run; 37 | proc print data=cars(read="badpw" obs=10); 38 | run; 39 | proc print data=cars(read="&read" obs=10); 40 | run; 41 | ``` 42 | 43 | 1. Assign libraries to RDBMS 44 | 45 | ``` 46 | %%prompt4var pw1 pw2 47 | libname foo teradata user=scott password=&pw1; 48 | libname bar oracle user=tiger password=&pw2; 49 | ``` 50 | -------------------------------------------------------------------------------- /sas_kernel/magics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/magics/__init__.py -------------------------------------------------------------------------------- /sas_kernel/magics/log_magic.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright SAS Institute 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the License); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | from metakernel import Magic 17 | 18 | class logMagic(Magic): 19 | def __init__(self, *args, **kwargs): 20 | super(logMagic, self).__init__(*args, **kwargs) 21 | 22 | def line_showLog(self): 23 | """ 24 | SAS Kernel magic to show the SAS log for the previous submitted code. 25 | This magic is only available within the SAS Kernel 26 | """ 27 | if self.kernel.mva is None: 28 | print("Can't show log because no session exists") 29 | else: 30 | return self.kernel._which_display(self.kernel.cachedlog) 31 | 32 | 33 | def line_showFullLog(self): 34 | """ 35 | SAS Kernel magic to show the entire SAS log since the kernel was started (last restarted) 36 | This magic is only available within the SAS Kernel 37 | """ 38 | if self.kernel.mva is None: 39 | self.kernel._allow_stdin = True 40 | self.kernel._start_sas() 41 | print("Session Started. Probably not the log you want.") 42 | return self.kernel._which_display(self.kernel.mva.saslog()) 43 | 44 | def register_magics(kernel): 45 | kernel.register_magics(logMagic) 46 | 47 | 48 | def register_ipython_magics(): 49 | from metakernel import IPythonKernel 50 | from IPython.core.magic import register_line_magic 51 | kernel = IPythonKernel() 52 | magic = logMagic(kernel) 53 | # Make magics callable: 54 | kernel.line_magics["showLog"] = magic 55 | kernel.line_magics["showFullLog"] = magic 56 | 57 | @register_line_magic 58 | def showLog(line): 59 | kernel.call_magic("%showLog " + line) 60 | 61 | @register_line_magic 62 | def showFullLog(line): 63 | kernel.call_magic("%showFullLog " + line) 64 | -------------------------------------------------------------------------------- /sas_kernel/magics/prompt4var_magic.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright SAS Institute 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the License); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | from collections import OrderedDict 17 | from metakernel import Magic 18 | 19 | class Prompt4VarMagic(Magic): 20 | def __init__(self, *args, **kwargs): 21 | super(Prompt4VarMagic, self).__init__(*args, **kwargs) 22 | 23 | def line_prompt4var(self, *args): 24 | """ 25 | %prompt4var - Prompt for macro variables that will 26 | be assigned to the SAS session. The variables will be 27 | prompted each time the line magic is executed. 28 | Example: 29 | %prompt4var libpath file1 30 | filename myfile "~&file1."; 31 | libname data "&libpath"; 32 | """ 33 | prmpt = OrderedDict() 34 | for arg in args: 35 | assert isinstance(arg, str) 36 | prmpt[arg] = False 37 | if not len(self.code): 38 | if self.kernel.mva is None: 39 | self.kernel._allow_stdin = True 40 | self.kernel._start_sas() 41 | self.kernel.mva.submit(code=self.code, results="html", prompt=prmpt) 42 | else: 43 | self.kernel.promptDict = prmpt 44 | 45 | def cell_prompt4var(self, *args): 46 | """ 47 | %%prompt4var - The cell magic prompts users for variables that are 48 | intended to be private -- passwords and such. The macro variables 49 | will be deleted from the when the cell finishes processing. 50 | Libnames assigned will still be active but the password will not 51 | be stored anywhere. 52 | 53 | Examples: 54 | %%prompt4var alter read 55 | data work.cars(alter="&alter" read="&read"); 56 | set sashelp.cars; 57 | id = _n_; 58 | run; 59 | proc print data=cars(read="badpw" obs=10); 60 | run; 61 | proc print data=cars(read="&read" obs=10); 62 | run; 63 | 64 | 65 | %%prompt4var pw1 pw2 66 | libname foo teradata user=scott password=&pw1; 67 | libname bar oracle user=tiger password=&pw2; 68 | """ 69 | prmpt = OrderedDict() 70 | for arg in args: 71 | assert isinstance(arg, str) 72 | prmpt[arg] = True 73 | if not len(self.code): 74 | if self.kernel.mva is None: 75 | self._allow_stdin = True 76 | self.kernel._start_sas() 77 | self.kernel.mva.submit(code=self.code, results="html", prompt=prmpt) 78 | else: 79 | self.kernel.promptDict = prmpt 80 | 81 | 82 | def register_magics(kernel): 83 | kernel.register_magics(Prompt4VarMagic) 84 | 85 | 86 | def register_ipython_magics(): 87 | from metakernel import IPythonKernel 88 | from IPython.core.magic import register_line_magic 89 | kernel = IPythonKernel() 90 | magic = Prompt4VarMagic(kernel) 91 | # Make magics callable: 92 | kernel.line_magics["prompt4var"] = magic 93 | 94 | @register_line_magic 95 | def prompt4var(line): 96 | kernel.call_magic("%prompt4var " + line) 97 | -------------------------------------------------------------------------------- /sas_kernel/magics/sas_session_magic.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright SAS Institute 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the License); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | from metakernel import Magic 17 | 18 | class SASsessionMagic(Magic): 19 | def __init__(self, *args, **kwargs): 20 | super(SASsessionMagic, self).__init__(*args, **kwargs) 21 | 22 | def line_SASsession(self, *args): 23 | """ 24 | SAS Kernel magic allows a programatic way to submit configuration 25 | details. 26 | This magic is only available within the SAS Kernel 27 | """ 28 | if len(args) > 1: 29 | args = ''.join(args) 30 | elif len(args) == 1: 31 | args = ''.join(args[0]) 32 | args = args.replace(' ', '') 33 | args = args.replace('"', '') 34 | args = args.replace("'", '') 35 | sess_params = dict(s.split('=') for s in args.split(',')) 36 | self.kernel._allow_stdin = True 37 | self.kernel._start_sas(**sess_params) 38 | 39 | def register_magics(kernel): 40 | kernel.register_magics(SASsessionMagic) 41 | 42 | 43 | def register_ipython_magics(): 44 | from metakernel import IPythonKernel 45 | from IPython.core.magic import register_line_magic 46 | kernel = IPythonKernel() 47 | magic = SASsessionMagic(kernel) 48 | # Make magics callable: 49 | kernel.line_magics["SASsession"] = magic 50 | 51 | @register_line_magic 52 | def SASsession(line): 53 | kernel.call_magic("%SASsession " + line) 54 | -------------------------------------------------------------------------------- /sas_kernel/nbextensions/README.md: -------------------------------------------------------------------------------- 1 | # SAS NBextensions 2 | 3 | 4 | ## Installing from PyPi 5 | With the release of Jupyter 4.2 (sas_kernel version 1.2) you can now install and enable nbextensions in a much improved way. 6 | To install and enable the showSASLog extension use the following command: 7 | ``` 8 | jupyter nbextension install --py sas_kernel.showSASLog 9 | jupyter nbextension enable sas_kernel.showSASLog --py 10 | ``` 11 | 12 | To install and enable the theme extension use the following command: 13 | ``` 14 | jupyter nbextension install --py sas_kernel.theme 15 | jupyter nbextension enable sas_kernel.theme --py 16 | ``` 17 | 18 | To verify the ebextensions you have installed use the following command: 19 | ``` 20 | jupyter nbextension list 21 | ``` 22 | 23 | If the extensions are correctly installed you will see output similar to this: 24 | ``` 25 | Known nbextensions: 26 | config dir: /root/.jupyter/nbconfig 27 | notebook section 28 | showSASLog/main enabled 29 | - Validating: OK 30 | theme/theme_selector enabled 31 | - Validating: OK 32 | ``` 33 | 34 | ## Installing from a cloned repository 35 | In your cloned repo you have a directory for each nbextension within the file structure as shown below: 36 | 37 | ``` 38 | sas_kernel 39 | | 40 | +-- showSASLog 41 | +-- theme 42 | ``` 43 | 44 | Extensions are installed from the command line. 45 | To install it systemwide use the following command (you must be root or have sudo privileges). This assumes you're in the nbextensions directory otherwise adjust your path. 46 | ``` 47 | jupyter nbextension install ./showSASLog 48 | ``` 49 | Which should display something similar to this (if you have super user rights): 50 | 51 | `copying showSASLog/main.js -> /usr/local/share/jupyter/nbextensions/main.js` 52 | 53 | 54 | To install for the current user only use the following command. Again assumes you're in the nbextensions directory otherwise adjust your path. 55 | ``` 56 | jupyter nbextension install ./showSASLog --user 57 | ``` 58 | Which should display something similar to this (if you DO NOT have super user rights): 59 | 60 | `copying showSASLog/main.js -> /home/sas/.local/share/jupyter/nbextensions/showSASLog/main.js` 61 | 62 | 63 | Then enable the notebook extension with the following command: 64 | ``` 65 | jupyter nbextension enable showSASLog 66 | ``` 67 | 68 | To disable (not that you'd ever want to): 69 | 70 | `jupyter nbextension disable showSASLog` 71 | 72 | ## Example 73 | There is a [notebook](../../notebook/loadSASExtensions.ipynb) that walks through the steps to install and enable the extensions 74 | 75 | -------------------------------------------------------------------------------- /sas_kernel/sasKernel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sassoftware/sas_kernel/6a799e5422e35b5f434f517fc60f26848f224345/sas_kernel/sasKernel.png -------------------------------------------------------------------------------- /sas_kernel/showSASLog/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright SAS Institute 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the License); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | 18 | def _jupyter_nbextension_paths(): 19 | return [dict(section="notebook", 20 | # relative path to the module directory 21 | src="", 22 | # directory in the `nbextension/` namespace 23 | dest="showSASLog", 24 | # _also_ in the `nbextension/` namespace 25 | require="showSASLog/main")] 26 | -------------------------------------------------------------------------------- /sas_kernel/showSASLog/main.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'base/js/namespace', 3 | 'jquery', 4 | 'services/config', 5 | 'base/js/events', 6 | 'base/js/utils' 7 | ], function(IPython, $, configmod, events, utils) { 8 | "use strict"; 9 | var base_url = utils.get_body_data("baseUrl"); 10 | var config = new configmod.ConfigSection('notebook', {base_url: base_url}); 11 | 12 | function handle_output(out){ 13 | var res = null; 14 | if(out.msg_type === "execute_result"){ 15 | res = out.content.data["text/plain"]; 16 | var logWindow = window.open('SASLog.html','_blank'); 17 | logWindow.document.open(); 18 | // strip the leading and trailing "'" 19 | logWindow.document.write(res.substring(1,res.length-1)); 20 | logWindow.document.close(); 21 | } 22 | } 23 | 24 | var SASlog = function () { 25 | var code_input = 'showSASLog_11092015'; 26 | var kernel = IPython.notebook.kernel; 27 | var callbacks = { 'iopub' : {'output' : handle_output}}; 28 | var msg_id = kernel.execute(code_input, callbacks, {silent:false}); 29 | }; 30 | var SASlogComplete = function () { 31 | var code_input = 'CompleteshowSASLog_11092015'; 32 | var kernel = IPython.notebook.kernel; 33 | var callbacks = { 'iopub' : {'output' : handle_output}}; 34 | var msg_id = kernel.execute(code_input, callbacks, {silent:false}); 35 | }; 36 | 37 | var load_ipython_extension = function() { 38 | config.load(); 39 | Jupyter.toolbar.add_buttons_group([ 40 | { 41 | id: 'showSASLog', 42 | label: 'Show the SAS Log for last executed cell', 43 | icon: 'fa-file-code-o', 44 | callback: SASlog 45 | }, 46 | { 47 | id: 'showSASLogComplete', 48 | label: 'Show the complete SAS Log for the notebook', 49 | icon: 'fa-history', 50 | callback: SASlogComplete 51 | } 52 | ]); 53 | }; 54 | return { 55 | load_ipython_extension : load_ipython_extension 56 | }; 57 | }); 58 | 59 | -------------------------------------------------------------------------------- /sas_kernel/showSASLog/readme.md: -------------------------------------------------------------------------------- 1 | ## Info 2 | Show the SAS log from either the 3 | last executed cell in the notebook ![](sasLogLast.PNG) 4 | OR 5 | the complete log for the notebook ![](sasLogComplete.PNG) 6 | 7 | 8 | 9 | ## Original Source 10 | This extension originally comes from [@jld23](https://github.com/jld23)'s [GitHub repository](https://github.com/jld23/IPython-notebook-extensions). 11 | 12 | 13 | ## License 14 | 15 | Copyright SAS Institute 16 | 17 | Licensed under the Apache License, Version 2.0 (the License); 18 | you may not use this file except in compliance with the License. 19 | You may obtain a copy of the License at 20 | 21 | http://www.apache.org/licenses/LICENSE-2.0 22 | 23 | Unless required by applicable law or agreed to in writing, software 24 | distributed under the License is distributed on an "AS IS" BASIS, 25 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 | See the License for the specific language governing permissions and 27 | limitations under the License. 28 | -------------------------------------------------------------------------------- /sas_kernel/showSASLog/showSASLog.yaml: -------------------------------------------------------------------------------- 1 | Type: IPython Notebook Extension 2 | Name: ShowSASLog 3 | Description: > 4 | Show the SAS log from either the 5 | last executed cell in the notebook 6 | OR 7 | the complete log for the notebook 8 | Icon: sasLogJoint.PNG 9 | Link: readme.md 10 | Main: main.js 11 | Compatibility: 4.x 12 | -------------------------------------------------------------------------------- /sas_kernel/theme/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright SAS Institute 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the License); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | 18 | def _jupyter_nbextension_paths(): 19 | return [dict(section="notebook", src="", dest="theme", require="theme/theme_selector")] 20 | -------------------------------------------------------------------------------- /sas_kernel/theme/base16-sas-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | Name: Base16 SAS Light 3 | Author: Jared Dean SAS Instititue 4 | CodeMirror template adapted for IPython Notebook by Jared Deam 5 | CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-chrome-devtools) 6 | */ 7 | 8 | /* 9 | Jupyter GLOBALS 10 | */ 11 | 12 | div.cell.edit_mode { 13 | border: thin #a1c659 solid !important; 14 | } 15 | 16 | .edit_mode div.cell.selected{ 17 | border-color: #fda331; 18 | } 19 | 20 | .CodeMirror pre { 21 | font-family: 'Menlo', 'Ubuntu Mono', monospace; 22 | font-size: 12px; 23 | } 24 | 25 | div.cell.selected { 26 | border-color: #76c7b7; 27 | } 28 | 29 | div.input_prompt { 30 | color: #6fb3d2; 31 | } 32 | 33 | div.output_prompt { 34 | color: #fb0120; 35 | } 36 | 37 | /* 38 | GLOBALS 39 | */ 40 | 41 | span.ansiblack {color: #303030;} 42 | span.ansiblue {color: #76c7b7;} 43 | span.ansigray {color: #d0d0d0;} 44 | span.ansigreen {color: #a1c659;} 45 | span.ansipurple {color: #d381c3;} 46 | span.ansired {color: #fb0120;} 47 | span.ansiyellow {color: #fda331;} 48 | 49 | div.output_stderr {background-color: #fb0120;} 50 | div.output_stderr pre {color: #f5f5f5;} 51 | 52 | .cm-s-ipython.CodeMirror {background: #ffffff; color: #303030;} 53 | .cm-s-ipython div.CodeMirror-selected {background: /*#f5f5f5*/ #CCCCCC !important;} 54 | .cm-s-ipython .CodeMirror-gutters {background: #ffffff; border-right: 0px;} 55 | .cm-s-ipython .CodeMirror-linenumber {color: /*#d0d0d0*/ #000000;} 56 | .cm-s-ipython .CodeMirror-cursor {border-left: 1px solid #b0b0b0 !important;} 57 | 58 | .cm-s-ipython span.cm-comment {color: #008000; font-weight: italic;} 59 | .cm-s-ipython span.cm-atom {color: #fb0120;} 60 | .cm-s-ipython span.cm-number {color: #fc6d24;} 61 | 62 | .cm-s-ipython span.cm-property, .cm-s-ipython span.cm-attribute {color: #a1c659;} 63 | .cm-s-ipython span.cm-keyword {color: #0000FF;} 64 | .cm-s-ipython span.cm-string {color: #800080;} 65 | .cm-s-ipython span.cm-operator {color: #000000;} 66 | .cm-s-ipython span.cm-builtin {color: #000080; font-weight: bold; } 67 | 68 | .cm-s-ipython span.cm-variable {color: #505050;} 69 | .cm-s-ipython span.cm-variable-2 {color: #000080;} 70 | .cm-s-ipython span.cm-def {color: #0000FF;} 71 | .cm-s-ipython span.cm-error {background: #fb0120; color: #b0b0b0;} 72 | .cm-s-ipython span.cm-bracket {color: #505050;} 73 | .cm-s-ipython span.cm-tag {color: #fb0120;} 74 | .cm-s-ipython span.cm-link {color: #d381c3;} 75 | .cm-s-ipython span.cm-meta {color: #fb0120;} 76 | 77 | .cm-s-ipython .CodeMirror-matchingbracket { text-decoration: underline; color: #303030 !important;} 78 | -------------------------------------------------------------------------------- /sas_kernel/theme/readme.md: -------------------------------------------------------------------------------- 1 | ## Info 2 | Create a toolbar so that different text themes can be selected from the UI. 3 | 4 | ## Original Source 5 | This extension originally comes from [@jld23](https://github.com/jld23)'s [GitHub repository](https://github.com/jld23/IPython-notebook-extensions). 6 | 7 | 8 | ## License 9 | 10 | The MIT License (MIT) 11 | 12 | Copyright (c) 2016 SAS Institute 13 | -------------------------------------------------------------------------------- /sas_kernel/theme/theme_selector.js: -------------------------------------------------------------------------------- 1 | /* 2 | * // Theme selector 3 | * // Select your favorite code coloring 4 | * // Author: Jared Dean SAS Instititue 5 | */ 6 | 7 | define(["require"], function(require) { 8 | "use strict"; 9 | 10 | var themes = { 11 | Default: "./theme_default.css", 12 | SAS_Light : "./base16-sas-light.css" 13 | }; 14 | 15 | 16 | function add_to_toolbar(current_theme) { 17 | 18 | var ipython_toolbar = $(IPython.toolbar.element), 19 | label = $('').addClass("navbar-text permissions-list").text('Theme:'), 20 | select = $('