mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-22 17:46:57 +03:00
Merge branch 'refs/heads/master' into sqlalchemy
This commit is contained in:
commit
961cb1ad83
14
.editorconfig
Normal file
14
.editorconfig
Normal file
|
@ -0,0 +1,14 @@
|
|||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{py,rst,ini}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
17
.gitignore
vendored
17
.gitignore
vendored
|
@ -61,3 +61,20 @@ target/
|
|||
|
||||
|
||||
/tests/django.sqlite
|
||||
|
||||
/graphene/index.json
|
||||
/graphene/meta.json
|
||||
|
||||
/meta.json
|
||||
/index.json
|
||||
|
||||
/docs/playground/graphene-js/pypyjs-release-nojit/
|
||||
/docs/static/playground/lib
|
||||
|
||||
/docs/static/playground
|
||||
|
||||
# PyCharm
|
||||
.idea
|
||||
|
||||
# Databases
|
||||
*.sqlite3
|
||||
|
|
91
.travis.yml
91
.travis.yml
|
@ -2,17 +2,92 @@ language: python
|
|||
sudo: false
|
||||
python:
|
||||
- 2.7
|
||||
- 3.3
|
||||
- 3.4
|
||||
- 3.5
|
||||
- pypy
|
||||
cache: pip
|
||||
cache:
|
||||
directories:
|
||||
- .cache/pip/
|
||||
- $HOME/.cache/pip
|
||||
- docs/node_modules/
|
||||
- $HOME/docs/node_modules
|
||||
before_install:
|
||||
- |
|
||||
git_diff=$(git diff --name-only $TRAVIS_COMMIT_RANGE)
|
||||
if [ "$?" == 0 ] && [ "$TEST_TYPE" != build_website ] && \
|
||||
! echo "$git_diff" | grep -qvE '(\.md$)|(^(docs))/'
|
||||
then
|
||||
echo "Only docs were updated, stopping build process."
|
||||
exit
|
||||
fi
|
||||
install:
|
||||
- pip install --cache-dir $HOME/.cache/pip pytest pytest-cov coveralls flake8 six blinker pytest-django
|
||||
- pip install --cache-dir $HOME/.cache/pip -e .[django]
|
||||
- python setup.py develop
|
||||
- |
|
||||
if [ "$TEST_TYPE" = build ]; then
|
||||
pip install --download-cache $HOME/.cache/pip/ pytest pytest-cov coveralls six pytest-django django-filter
|
||||
pip install --download-cache $HOME/.cache/pip/ -e .[django]
|
||||
pip install django==$DJANGO_VERSION
|
||||
python setup.py develop
|
||||
elif [ "$TEST_TYPE" = build_website ]; then
|
||||
pip install --download-cache $HOME/.cache/pip/ -e .
|
||||
python setup.py develop
|
||||
elif [ "$TEST_TYPE" = lint ]; then
|
||||
pip install --download-cache $HOME/.cache/pip/ flake8
|
||||
fi
|
||||
script:
|
||||
- py.test --cov=graphene
|
||||
- flake8
|
||||
- |
|
||||
if [ "$TEST_TYPE" = build_website ]; then
|
||||
if [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||
echo "Building the web."
|
||||
nvm install 4.0
|
||||
|
||||
GH_PAGES_DIR="$TRAVIS_BUILD_DIR"/docs/public
|
||||
git config --global user.name "Travis CI"
|
||||
git config --global user.email "travis@graphene-python.org"
|
||||
git clone --branch gh-pages --depth=50 \
|
||||
https://graphql-python-bot@github.com/graphql-python/graphene.git \
|
||||
$GH_PAGES_DIR
|
||||
cd docs
|
||||
./playground/graphene-js/build.sh
|
||||
npm run deploy
|
||||
cd $GH_PAGES_DIR
|
||||
git status
|
||||
git add --intent-to-add .
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
git add -A .
|
||||
git commit -m "Rebuild website"
|
||||
git push "https://${GITHUB_TOKEN}@github.com/graphql-python/graphene.git" gh-pages
|
||||
fi
|
||||
exit
|
||||
fi
|
||||
elif [ "$TEST_TYPE" = lint ]; then
|
||||
echo "Checking Python code lint."
|
||||
flake8
|
||||
exit
|
||||
elif [ "$TEST_TYPE" = build ]; then
|
||||
py.test --cov=graphene
|
||||
fi
|
||||
after_success:
|
||||
- coveralls
|
||||
- |
|
||||
if [ "$TEST_TYPE" = build ]; then
|
||||
coveralls
|
||||
fi
|
||||
env:
|
||||
matrix:
|
||||
- TEST_TYPE=build
|
||||
global:
|
||||
secure: SQC0eCWCWw8bZxbLE8vQn+UjJOp3Z1m779s9SMK3lCLwJxro/VCLBZ7hj4xsrq1MtcFO2U2Kqf068symw4Hr/0amYI3HFTCFiwXAC3PAKXeURca03eNO2heku+FtnQcOjBanExTsIBQRLDXMOaUkf3MIztpLJ4LHqMfUupKmw9YSB0v40jDbSN8khBnndFykmOnVVHznFp8USoN5F0CiPpnfEvHnJkaX76lNf7Kc9XNShBTTtJsnsHMhuYQeInt0vg9HSjoIYC38Tv2hmMj1myNdzyrHF+LgRjI6ceGi50ApAnGepXC/DNRhXROfECKez+LON/ZSqBGdJhUILqC8A4WmWmIjNcwitVFp3JGBqO7LULS0BI96EtSLe8rD1rkkdTbjivajkbykM1Q0Tnmg1adzGwLxRUbTq9tJQlTTkHBCuXIkpKb1mAtb/TY7A6BqfnPi2xTc/++qEawUG7ePhscdTj0IBrUfZsUNUYZqD8E8XbSWKIuS3SHE+cZ+s/kdAsm4q+FFAlpZKOYGxIkwvgyfu4/Plfol4b7X6iAP9J3r1Kv0DgBVFst5CXEwzZs19/g0CgokQbCXf1N+xeNnUELl6/fImaR3RKP22EaABoil4z8vzl4EqxqVoH1nfhE+WlpryXsuSaF/1R+WklR7aQ1FwoCk8V8HxM2zrj4tI8k=
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=build DJANGO_VERSION=1.6
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=build DJANGO_VERSION=1.7
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=build DJANGO_VERSION=1.8
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=build DJANGO_VERSION=1.9
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=build_website
|
||||
- python: '2.7'
|
||||
env: TEST_TYPE=lint
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# ![Graphene Logo](http://graphene-python.org/favicon.png) [Graphene](http://graphene-python.org) [![Build Status](https://travis-ci.org/graphql-python/graphene.svg?branch=master)](https://travis-ci.org/graphql-python/graphene) [![PyPI version](https://badge.fury.io/py/graphene.svg)](https://badge.fury.io/py/graphene) [![Coverage Status](https://coveralls.io/repos/graphql-python/graphene/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-python/graphene?branch=master)
|
||||
|
||||
|
||||
Graphene is a Python library for building GraphQL schemas/types fast and easily.
|
||||
[Graphene](http://graphene-python.org) is a Python library for building GraphQL schemas/types fast and easily.
|
||||
|
||||
- **Easy to use:** Graphene helps you use GraphQL in Python without effort.
|
||||
- **Relay:** Graphene has builtin support for Relay
|
||||
|
@ -10,6 +10,7 @@ Graphene is a Python library for building GraphQL schemas/types fast and easily.
|
|||
|
||||
*What is supported in this Python version?* **Everything**: Interfaces, ObjectTypes, Scalars, Unions and Relay (Nodes, Connections), in addition to queries, mutations and subscriptions.
|
||||
|
||||
**NEW**!: [Try graphene online](http://graphene-python.org/playground/)
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|Graphene Logo| `Graphene <http://graphene-python.org>`__ |Build Status| |PyPI version| |Coverage Status|
|
||||
=========================================================================================================
|
||||
|
||||
Graphene is a Python library for building GraphQL schemas/types fast and
|
||||
easily.
|
||||
`Graphene <http://graphene-python.org>`__ is a Python library for
|
||||
building GraphQL schemas/types fast and easily.
|
||||
|
||||
- **Easy to use:** Graphene helps you use GraphQL in Python without
|
||||
effort.
|
||||
|
@ -16,6 +16,9 @@ easily.
|
|||
ObjectTypes, Scalars, Unions and Relay (Nodes, Connections), in addition
|
||||
to queries, mutations and subscriptions.
|
||||
|
||||
**NEW**!: `Try graphene
|
||||
online <http://graphene-python.org/playground/>`__
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
autoflake ./ -r --remove-unused-variables --remove-all-unused-imports --in-place
|
||||
autopep8 ./ -r --in-place --experimental --aggressive --max-line-length 120
|
||||
isort -rc .
|
||||
# Install the required scripts with
|
||||
# pip install autoflake autopep8 isort
|
||||
autoflake ./examples/ ./graphene/ -r --remove-unused-variables --remove-all-unused-imports --in-place
|
||||
autopep8 ./examples/ ./graphene/ -r --in-place --experimental --aggressive --max-line-length 120
|
||||
isort -rc ./examples/ ./graphene/
|
||||
|
|
3
docs/.babelrc
Normal file
3
docs/.babelrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"stage": 0
|
||||
}
|
29
docs/.gitignore
vendored
Normal file
29
docs/.gitignore
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directory
|
||||
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
public/
|
43
docs/README.md
Normal file
43
docs/README.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Graphene Docs
|
||||
|
||||
Graphene docs are powered by [gatsby](https://github.com/gatsbyjs/gatsby).
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
For running locally this docs. You have to execute
|
||||
```bash
|
||||
npm install -g gatsby && npm install
|
||||
```
|
||||
|
||||
And then
|
||||
|
||||
```bash
|
||||
gatsby develop
|
||||
```
|
||||
|
||||
## Playground
|
||||
|
||||
If you want to have the playground running too, just execute
|
||||
|
||||
```
|
||||
./playground/graphene-js/build.sh
|
||||
```
|
||||
|
||||
This command will clone the [pypyjs-release-nojit](https://github.com/pypyjs/pypyjs-release-nojit) repo, update it with the latest graphene, graphql-core and graphql-relay code, and make it available for the `/playground` view in the docs.
|
||||
|
||||
|
||||
## Build
|
||||
|
||||
For building the docs into the `public` dir, just run:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
|
||||
## Automation
|
||||
|
||||
Thanks to [Travis](https://github.com/graphql-python/graphene/blob/master/.travis.yml#L39-L58), we automated the way documentation is updated in the `gh-pages` branch.
|
||||
|
||||
Each time we modify the docs in the `master` branch the travis job runs and updates the `gh-pages` branch with the latest code, so [Graphene's website](http://graphene-python.org) have always the latest docs.
|
19
docs/app.js
Normal file
19
docs/app.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
exports.loadContext = function(callback) {
|
||||
var context;
|
||||
context = require.context('./pages', true);
|
||||
if (module.hot) {
|
||||
module.hot.accept(context.id, function() {
|
||||
context = require.context('./pages', true);
|
||||
return callback(context);
|
||||
});
|
||||
}
|
||||
return callback(context);
|
||||
};
|
||||
|
||||
exports.onRouteChange = function(state) {
|
||||
if (typeof window !== "undefined" && window.ga) {
|
||||
window.ga('send', 'pageview', {
|
||||
page: state.pathname
|
||||
});
|
||||
}
|
||||
}
|
7
docs/assets/icon.js
Normal file
7
docs/assets/icon.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import React from 'react';
|
||||
|
||||
export default class Icon extends React.Component {
|
||||
render() {
|
||||
return <span {...this.props} src={null} dangerouslySetInnerHTML={{__html:this.props.src}} />
|
||||
}
|
||||
}
|
10
docs/assets/logo.svg
Normal file
10
docs/assets/logo.svg
Normal file
|
@ -0,0 +1,10 @@
|
|||
<svg width="100%" height="100%" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" class="logo"><g fill="none" fill-rule="evenodd"><path class="logo-path" d="M49.17 10.915L15.415 30.29l.058 39.252 34.633 20.082 33.345-18.95-.023-39.79-34.185 19.238" stroke="white" stroke-width="4.987"/>
|
||||
<g transform="translate(41.5 3)"><ellipse cx="7.934" cy="7.97" rx="7.934" ry="7.97" fill="white"/></g>
|
||||
<g transform="translate(8 22.482)"><ellipse cx="7.934" cy="7.97" rx="7.934" ry="7.97" fill="white"/></g>
|
||||
<g transform="translate(8 62.33)"><ellipse cx="7.934" cy="7.97" rx="7.934" ry="7.97" fill="white"/></g>
|
||||
<g transform="translate(42.382 81.813)"><ellipse cx="7.934" cy="7.97" rx="7.934" ry="7.97" fill="white"/></g>
|
||||
<g transform="translate(75.882 62.33)"><ellipse cx="7.934" cy="7.97" rx="7.934" ry="7.97" fill="white"/></g>
|
||||
<g transform="translate(75.882 22.482)"><ellipse cx="7.934" cy="7.97" rx="7.934" ry="7.97" fill="white"/></g>
|
||||
<g transform="translate(42.382 41.964)"><ellipse cx="7.934" cy="7.97" rx="7.934" ry="7.97" fill="white"/></g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
25
docs/config.toml
Normal file
25
docs/config.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
siteTitle = "Graphene"
|
||||
ga = "UA-12613282-7"
|
||||
|
||||
[docs.quickstart]
|
||||
name = "Quickstart"
|
||||
pages = [
|
||||
"/docs/quickstart/",
|
||||
]
|
||||
|
||||
[docs.walkthrough]
|
||||
name = "Walkthrough"
|
||||
pages = [
|
||||
"/docs/interfaces/",
|
||||
"/docs/objecttypes/",
|
||||
"/docs/mutations/",
|
||||
"/docs/basic-types/",
|
||||
"/docs/relay/",
|
||||
]
|
||||
|
||||
[docs.django]
|
||||
name = "Django"
|
||||
pages = [
|
||||
"/docs/django/tutorial/",
|
||||
"/docs/django/filtering/",
|
||||
]
|
41
docs/css/bm.css
Normal file
41
docs/css/bm.css
Normal file
|
@ -0,0 +1,41 @@
|
|||
/* Position and sizing of burger button */
|
||||
.bm-burger-button {
|
||||
position: absolute;
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
right: 36px;
|
||||
top: 42px;
|
||||
}
|
||||
|
||||
/* Color/shape of burger icon bars */
|
||||
.bm-burger-bars {
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* Color of close button cross */
|
||||
.bm-cross {
|
||||
background: #bdc3c7;
|
||||
margin-top: -1px;
|
||||
width: 1px!important;
|
||||
height: 18px!important;
|
||||
}
|
||||
|
||||
/* Background color of sidebar */
|
||||
.bm-menu {
|
||||
background: #3c3c3c;
|
||||
box-shadow: -1px 0 5px rgba(0,0,0,.15);
|
||||
}
|
||||
|
||||
/* Morph shape necessary with bubble or elastic */
|
||||
.bm-morph-shape {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.bm-menu-wrap {
|
||||
z-index: 10000!important;
|
||||
}
|
||||
/* General menu styles */
|
||||
.bm-menu {
|
||||
padding: 2.5em 1.5em 0;
|
||||
font-size: 1.15em;
|
||||
}
|
1079
docs/css/graphiql.css
Normal file
1079
docs/css/graphiql.css
Normal file
File diff suppressed because it is too large
Load Diff
70
docs/css/hljs.css
Normal file
70
docs/css/hljs.css
Normal file
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* GitHub Gist Theme
|
||||
* Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
BIN
docs/css/images/edit.png
Normal file
BIN
docs/css/images/edit.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 372 B |
BIN
docs/css/images/edit@2x.png
Normal file
BIN
docs/css/images/edit@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 688 B |
BIN
docs/css/images/starwars-icon.png
Normal file
BIN
docs/css/images/starwars-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
docs/css/images/starwars-icon@2x.png
Normal file
BIN
docs/css/images/starwars-icon@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
426
docs/css/main.styl
Normal file
426
docs/css/main.styl
Normal file
|
@ -0,0 +1,426 @@
|
|||
@import 'nib'
|
||||
@import 'jeet'
|
||||
|
||||
@import 'https://fonts.googleapis.com/css?family=Raleway:400,500,600,200,100&.css'
|
||||
|
||||
normalize-css()
|
||||
|
||||
@import 'hljs.css'
|
||||
@import 'bm.css'
|
||||
|
||||
$wrapper
|
||||
center(960px, pad:20px)
|
||||
|
||||
.wrapper
|
||||
@extend $wrapper
|
||||
position relative
|
||||
|
||||
a, a:hover
|
||||
text-decoration none
|
||||
|
||||
a
|
||||
color rgb(42,93,173)
|
||||
|
||||
p
|
||||
margin-bottom 1em
|
||||
|
||||
html, body
|
||||
font-family "Helvetica Neue", Helvetica, Arial, sans-serif
|
||||
font-weight 300
|
||||
font-size 16px
|
||||
color #606060
|
||||
line-height 1.5
|
||||
height 100%
|
||||
margin 0
|
||||
width 100%
|
||||
|
||||
.header
|
||||
clearfix()
|
||||
position relative
|
||||
text-align center
|
||||
background: #DB594C;
|
||||
background-image: radial-gradient(95% 101%, #E46643 5%, rgba(226,91,72,0.00) 100%)
|
||||
.logo
|
||||
width 42px
|
||||
height @width
|
||||
vertical-align middle
|
||||
|
||||
h1
|
||||
max-width 380px
|
||||
font-family 'Raleway', sans-serif
|
||||
font-weight 200
|
||||
font-size 42px
|
||||
color #FFFFFF
|
||||
line-height 49px
|
||||
margin 80px auto 40px
|
||||
z-index 110
|
||||
|
||||
.get-started
|
||||
font-family 'Raleway'
|
||||
display inline-block
|
||||
margin 0 auto
|
||||
font-size 13px
|
||||
color #FFFFFF
|
||||
padding 0 18px
|
||||
text-transform uppercase
|
||||
font-weight 600
|
||||
line-height 15px
|
||||
border 1px solid #FFFFFF
|
||||
border-radius 2px
|
||||
padding 12px 18px
|
||||
z-index 111
|
||||
position relative
|
||||
&:hover
|
||||
background white
|
||||
color #E05B49
|
||||
text-decoration none
|
||||
|
||||
.header-wrapper
|
||||
@extend $wrapper
|
||||
text-align left
|
||||
padding-top 32px
|
||||
padding-bottom 32px
|
||||
position relative
|
||||
z-index 100
|
||||
|
||||
.header-extended
|
||||
padding-bottom 100px
|
||||
|
||||
.header-nav
|
||||
margin-top 8px
|
||||
a
|
||||
font-family 'Raleway'
|
||||
font-size 13px
|
||||
color #FFFFFF
|
||||
margin 0 16px
|
||||
padding 0 2px
|
||||
text-transform uppercase
|
||||
font-weight 600
|
||||
line-height 15px
|
||||
position relative
|
||||
&.active:before
|
||||
content: ''
|
||||
width 5px
|
||||
height 5px
|
||||
border-radius 3px
|
||||
display block
|
||||
position absolute
|
||||
background white
|
||||
left 50%
|
||||
margin-left -3px
|
||||
bottom -24px
|
||||
|
||||
+below(600px)
|
||||
display none
|
||||
|
||||
.bm-burger-button, .bm-menu-wrap, .bm-overlay
|
||||
display none
|
||||
+below(600px)
|
||||
display block
|
||||
|
||||
.bm-burger-button
|
||||
z-index 300!important
|
||||
|
||||
.bm-overlay
|
||||
z-index 1000!important
|
||||
|
||||
.bm-item-list
|
||||
a
|
||||
font-family 'Raleway'
|
||||
display block
|
||||
font-size 15px
|
||||
color #CCC
|
||||
margin 6px 0
|
||||
padding 10px 6px
|
||||
text-transform uppercase
|
||||
font-weight 500
|
||||
line-height 20px
|
||||
position relative
|
||||
&:hover
|
||||
color white
|
||||
|
||||
.header-logo
|
||||
font-family 'Raleway'
|
||||
font-size 22px
|
||||
color #FFFFFF
|
||||
float left
|
||||
font-weight 500
|
||||
text-transform uppercase
|
||||
text-decoration none
|
||||
|
||||
.header-nav
|
||||
float right
|
||||
|
||||
.logo
|
||||
path
|
||||
stroke-dasharray 250
|
||||
stroke-dashoffset 250
|
||||
animation logo-dash .9s ease-in-out forwards
|
||||
animation-delay .12s
|
||||
g
|
||||
ellipse
|
||||
animation logo-dot .3s ease forwards
|
||||
animation-fill-mode both
|
||||
transform-origin 50% 50%
|
||||
&:nth-child(2)
|
||||
ellipse
|
||||
animation-delay .1s
|
||||
&:nth-child(3)
|
||||
ellipse
|
||||
animation-delay .2s
|
||||
&:nth-child(4)
|
||||
ellipse
|
||||
animation-delay .3s
|
||||
&:nth-child(5)
|
||||
ellipse
|
||||
animation-delay .4s
|
||||
&:nth-child(6)
|
||||
ellipse
|
||||
animation-delay .5s
|
||||
&:nth-child(7)
|
||||
ellipse
|
||||
animation-delay .6s
|
||||
&:nth-child(8)
|
||||
ellipse
|
||||
animation-delay .7s
|
||||
|
||||
@keyframes logo-dash
|
||||
to
|
||||
stroke-dashoffset 0
|
||||
|
||||
@keyframes logo-dot
|
||||
from
|
||||
opacity 0.5
|
||||
transform scale(0)
|
||||
to
|
||||
opacity 1
|
||||
transform scale(1)
|
||||
|
||||
#header-background
|
||||
z-index 0
|
||||
display block
|
||||
position absolute
|
||||
width 100%
|
||||
top 0
|
||||
bottom 0
|
||||
right 0
|
||||
left 0
|
||||
|
||||
.particles-js-canvas-el
|
||||
display block
|
||||
opacity 0
|
||||
position absolute
|
||||
|
||||
.starwars-example-wrapper
|
||||
+below(600px)
|
||||
margin-bottom 30px
|
||||
|
||||
.starwars-example
|
||||
background: #3C3C3C
|
||||
display inline-block
|
||||
position absolute
|
||||
right 20px
|
||||
top -100px
|
||||
box-shadow: 0px 2px 5px 0px rgba(0,0,0,0.25);
|
||||
border-radius: 100px
|
||||
font-size 13px
|
||||
padding 17px 17px 17px 71px
|
||||
width 236px
|
||||
box-sizing border-box
|
||||
color white
|
||||
font-family 'Raleway'
|
||||
font-weight 500
|
||||
transition all .2s ease-in-out
|
||||
&:before
|
||||
content: ''
|
||||
display block
|
||||
position absolute
|
||||
left 20px
|
||||
top 20px
|
||||
height 32px
|
||||
width 32px
|
||||
image './images/starwars-icon.png' 32px 32px
|
||||
|
||||
&:hover
|
||||
transform translateY(-3px)
|
||||
box-shadow 0px 4px 8px 0px rgba(0,0,0,0.32)
|
||||
|
||||
+below(600px)
|
||||
top -70px
|
||||
|
||||
.improve-document-link
|
||||
position fixed
|
||||
right 0
|
||||
bottom 70px
|
||||
transform-origin 100% 100%
|
||||
background: #999;
|
||||
border: 1px solid #919191;
|
||||
border-radius: 3px 3px 0 0;
|
||||
border-bottom 0
|
||||
padding 9px 12px 12px 34px
|
||||
transform: rotate(270deg) translateX(100%) translateY(3px);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
text-transform uppercase
|
||||
color: #FFFFFF;
|
||||
letter-spacing: 0.3px;
|
||||
line-height: 11px;
|
||||
transition all .2s ease-in-out
|
||||
&:before
|
||||
content: ''
|
||||
display block
|
||||
position absolute
|
||||
left 10px
|
||||
top 8px
|
||||
height 16px
|
||||
width 16px
|
||||
image './images/edit.png' 16px 16px
|
||||
&:hover
|
||||
transform: rotate(270deg) translateX(100%)
|
||||
background #666
|
||||
border-color #555
|
||||
+below(600px)
|
||||
display none
|
||||
|
||||
$title
|
||||
display block
|
||||
font-family: 'Raleway';
|
||||
font-weight 500
|
||||
line-height 1.2em
|
||||
padding-top .3em
|
||||
margin-bottom .5em
|
||||
padding-bottom .5em
|
||||
color #4A4A4A
|
||||
|
||||
.markdown
|
||||
.wrapper
|
||||
margin-top 60px
|
||||
+below(600px)
|
||||
margin-top 30px
|
||||
|
||||
h1, h2, h3, h4, h5, h6
|
||||
@extend $title
|
||||
|
||||
h1
|
||||
font-size 32px
|
||||
h2
|
||||
font-size 26px
|
||||
h3
|
||||
font-size 24px
|
||||
h4
|
||||
font-size 21px
|
||||
h5
|
||||
font-size 18px
|
||||
h6
|
||||
font-size 16px
|
||||
strong
|
||||
font-weight 500
|
||||
pre
|
||||
line-height 20px
|
||||
background #FAFAFA
|
||||
padding 20px
|
||||
white-space: pre
|
||||
display: block;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
p code, ul code
|
||||
background #FAFAFA
|
||||
padding 2px 4px
|
||||
border-radius 2px
|
||||
border 1px solid #CCC
|
||||
color #000
|
||||
p + p, p + ul
|
||||
margin-top -.4em
|
||||
p + ul
|
||||
margin-top -.6em
|
||||
code
|
||||
font-size 14px
|
||||
line-height 20px
|
||||
overflow-x: auto;
|
||||
margin-bottom 40px
|
||||
|
||||
.markdown h1:first-child
|
||||
margin-top 0
|
||||
padding-top 0
|
||||
|
||||
.page-title
|
||||
background: #F9F9F9;
|
||||
padding 48px 0
|
||||
|
||||
h1
|
||||
margin 0 auto
|
||||
@extend $wrapper
|
||||
font-family: 'Raleway';
|
||||
font-size: 40px;
|
||||
font-weight 200
|
||||
color: #585858;
|
||||
line-height: 50px;
|
||||
|
||||
+below(600px)
|
||||
padding 30px 0
|
||||
|
||||
.docs
|
||||
@extend $wrapper
|
||||
|
||||
.docs-aside
|
||||
col(1/4)
|
||||
margin-top 60px
|
||||
+below(600px)
|
||||
padding 20px
|
||||
width 100%
|
||||
box-sizing content-box
|
||||
margin 0 -20px
|
||||
margin-bottom 30px
|
||||
background #F9F9F9
|
||||
|
||||
.docs-aside-group
|
||||
display block
|
||||
margin-bottom 40px
|
||||
h3
|
||||
font-family: 'Raleway';
|
||||
font-weight 500
|
||||
font-size 12px
|
||||
text-transform uppercase
|
||||
line-height 1.2em
|
||||
margin-bottom 1em
|
||||
color #AAA
|
||||
a
|
||||
display block
|
||||
font-size 15px
|
||||
font-weight 400
|
||||
line-height 22px
|
||||
height 28px
|
||||
padding 3px 0
|
||||
color #4A4A4A
|
||||
&.active
|
||||
font-weight 500
|
||||
line-height 21px
|
||||
color #E05B49
|
||||
|
||||
+below(600px)
|
||||
display none
|
||||
|
||||
.docs-aside-navselect
|
||||
margin-top -18px
|
||||
display none
|
||||
width 100%
|
||||
+below(600px)
|
||||
display block
|
||||
|
||||
.docs-content
|
||||
col(3/4)
|
||||
margin-top 60px
|
||||
margin-bottom 20px
|
||||
+below(600px)
|
||||
margin-top 10px
|
||||
col(1)
|
||||
|
||||
>h1
|
||||
margin 0
|
||||
@extend $title
|
||||
font-size 32px
|
||||
|
||||
.docs-next
|
||||
float right
|
||||
color #e05b49
|
||||
font-weight 400
|
148
docs/css/playground.styl
Normal file
148
docs/css/playground.styl
Normal file
|
@ -0,0 +1,148 @@
|
|||
@import 'nib'
|
||||
@import 'graphiql.css'
|
||||
|
||||
.playground
|
||||
position absolute
|
||||
top 106px
|
||||
left 0
|
||||
right 0
|
||||
bottom 0
|
||||
display flex
|
||||
flex-direction row
|
||||
min-width 960px
|
||||
.loading
|
||||
position absolute
|
||||
display block
|
||||
left 0
|
||||
right 0
|
||||
bottom 0
|
||||
top 0
|
||||
z-index 10000
|
||||
background rgba(255,255,255,.6)
|
||||
|
||||
.playground-schema
|
||||
min-width 400px
|
||||
width 36%
|
||||
border-right 1px solid #E0E0E0
|
||||
// box-shadow 0 0 8px rgba(0, 0, 0, 0.15)
|
||||
position relative
|
||||
z-index 100
|
||||
display flex
|
||||
flex-direction: column
|
||||
|
||||
.cm-def
|
||||
.cm-variable + .cm-keyword // lambda
|
||||
&:not(.CodeMirror-lint-mark-error)
|
||||
transition all .3s ease-in-out
|
||||
background transparent
|
||||
|
||||
.activeline
|
||||
.cm-def
|
||||
.cm-variable + .cm-keyword // lambda
|
||||
$color = #D7D3F1
|
||||
// $color = rgba(219, 89, 76, .2)
|
||||
background $color
|
||||
border-radius 1px
|
||||
box-shadow 0 0 0 2px $color
|
||||
|
||||
.playground-schema-editor
|
||||
flex 1
|
||||
position relative
|
||||
.CodeMirror
|
||||
font-size: 13px;
|
||||
position absolute
|
||||
height 100%
|
||||
width 100%
|
||||
top 0
|
||||
left 0
|
||||
right 0
|
||||
bottom 0
|
||||
font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace;
|
||||
color: #141823;
|
||||
.CodeMirror-lines
|
||||
padding 20px 0
|
||||
|
||||
.playground-schema-header
|
||||
// height 48px
|
||||
// font-family 'Raleway', sans-serif
|
||||
// font-weight 300
|
||||
// line-height 48px
|
||||
// padding 0 10px
|
||||
// border-bottom solid 1px #d0d0d0
|
||||
height: 48px;
|
||||
box-sizing border-box
|
||||
font-family: 'Raleway', sans-serif;
|
||||
color: #999;
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
line-height: 52px;
|
||||
padding: 0 12px;
|
||||
border-bottom: solid 1px #d0d0d0;
|
||||
background: #F9F9F9;
|
||||
|
||||
.playground-graphiql
|
||||
flex 1
|
||||
height 100%
|
||||
|
||||
|
||||
.cm-s-graphene
|
||||
|
||||
/* Comment */
|
||||
.cm-s-graphene .cm-comment
|
||||
color: #999;
|
||||
|
||||
/* Punctuation */
|
||||
.cm-s-graphene .cm-punctuation
|
||||
color: #555;
|
||||
|
||||
/* Keyword */
|
||||
.cm-s-graphene .cm-keyword
|
||||
// color: #B11A04;
|
||||
// color #D2054E
|
||||
color #a71d5d
|
||||
|
||||
/* OperationName, FragmentName */
|
||||
.cm-s-graphene .cm-def
|
||||
// color: #D2054E;
|
||||
color: #1F61A0;
|
||||
|
||||
/* FieldName */
|
||||
.cm-s-graphene .cm-property
|
||||
color: #333;
|
||||
|
||||
/* FieldAlias */
|
||||
.cm-s-graphene .cm-qualifier
|
||||
color: #1C92A9;
|
||||
|
||||
/* ArgumentName and ObjectFieldName */
|
||||
.cm-s-graphene .cm-attribute
|
||||
color: #8B2BB9;
|
||||
|
||||
/* Number */
|
||||
.cm-s-graphene .cm-number
|
||||
color: #2882F9;
|
||||
|
||||
/* String */
|
||||
.cm-s-graphene .cm-string
|
||||
color: #D64292;
|
||||
|
||||
/* Boolean */
|
||||
.cm-s-graphene .cm-builtin
|
||||
color: #D47509;
|
||||
|
||||
/* EnumValue */
|
||||
.cm-s-graphene .cm-string-2
|
||||
color: #0B7FC7;
|
||||
|
||||
/* Variable */
|
||||
.cm-s-graphene .cm-variable
|
||||
color: #333;
|
||||
|
||||
/* Directive */
|
||||
.cm-s-graphene .cm-meta
|
||||
color: #B33086;
|
||||
|
||||
/* Type */
|
||||
.cm-s-graphene .cm-atom
|
||||
color: #CA9800;
|
72
docs/gatsby.config.js
Normal file
72
docs/gatsby.config.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
var nib = require("nib");
|
||||
var jeet = require("jeet");
|
||||
var rupture = require("rupture");
|
||||
var path = require("path");
|
||||
var ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||
var webpack = require("webpack");
|
||||
var CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
module.exports = function(config, env) {
|
||||
var IS_STATIC = env === 'static';
|
||||
var entry = config._config.entry.slice();
|
||||
var publicPath = config._config.output.publicPath;
|
||||
// var output = config._config.output;
|
||||
// output.filename = "[name].js";
|
||||
config._config.entry = {
|
||||
bundle: entry,
|
||||
};
|
||||
config.merge({
|
||||
stylus: {
|
||||
use: [nib(), jeet(), rupture()]
|
||||
},
|
||||
output: {
|
||||
filename: "[name].js",
|
||||
publicPath: "/",
|
||||
},
|
||||
resolveLoader: {
|
||||
root: path.join(__dirname, "node_modules"),
|
||||
modulesDirectories: ['./'],
|
||||
},
|
||||
resolve: {
|
||||
root: path.join(__dirname, "node_modules"),
|
||||
alias: {
|
||||
'original-react': path.join(__dirname, "node_modules", "react"),
|
||||
'react/lib': path.join(__dirname, "node_modules", "react", "lib"),
|
||||
'react': path.join(__dirname, 'patched-react.js'),
|
||||
'pypyjs': '../playground/graphene-js/pypyjs',
|
||||
'playground-page': (env != "static")?'../playground/page':'../pages/_empty',
|
||||
'playground-wrapper': (env == "develop")?'../playground/page':'../playground/wrapper',
|
||||
},
|
||||
modulesDirectories: ['./']
|
||||
}
|
||||
});
|
||||
if (IS_STATIC) {
|
||||
config.plugin('extract-css', ExtractTextPlugin, ["app.css"]);
|
||||
}
|
||||
config.plugin('static', CopyWebpackPlugin, [[{ from: '../static'}]]);
|
||||
config.plugin('define-env', webpack.DefinePlugin, [{
|
||||
"ENV": JSON.stringify(env),
|
||||
"PUBLIC_PATH": JSON.stringify(publicPath),
|
||||
}]);
|
||||
// if (env != "static") {
|
||||
// config.plugin('commons', webpack.optimize.CommonsChunkPlugin, ["commons.js"]);
|
||||
// }
|
||||
|
||||
config.loader('stylus', function(cfg) {
|
||||
cfg.test = /\.styl$/;
|
||||
if (IS_STATIC) {
|
||||
cfg.loader = ExtractTextPlugin.extract('style-loader', 'css-loader!stylus-loader', { allChunks: true });
|
||||
}
|
||||
else {
|
||||
cfg.loader = 'style-loader!css-loader!stylus-loader';
|
||||
}
|
||||
return cfg
|
||||
});
|
||||
config.removeLoader('png');
|
||||
config.loader('png', function(cfg) {
|
||||
cfg.test = /\.png$/;
|
||||
cfg.loader = 'url-loader'
|
||||
return cfg
|
||||
})
|
||||
return config;
|
||||
};
|
35
docs/html.js
Normal file
35
docs/html.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import React from 'react';
|
||||
import DocumentTitle from 'react-document-title';
|
||||
|
||||
export default class Html extends React.Component {
|
||||
render() {
|
||||
var title = this.props.title || DocumentTitle.rewind();
|
||||
return (
|
||||
<html lang={this.props.lang}>
|
||||
<head>
|
||||
<meta charSet="utf-8"/>
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0 maximum-scale=1.0'/>
|
||||
<title>{title}</title>
|
||||
<link rel="shortcut icon" href="/favicon.png"/>
|
||||
<link href='https://fonts.googleapis.com/css?family=Raleway:400,600,200,100' rel='stylesheet' type='text/css' />
|
||||
<link href='/app.css' rel='stylesheet' type='text/css' />
|
||||
</head>
|
||||
<body>
|
||||
<div id="react-mount" dangerouslySetInnerHTML={{__html: this.props.body}} />
|
||||
<script src="/bundle.js"/>
|
||||
{this.props.config.ga?<script dangerouslySetInnerHTML={{__html: `
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', '${this.props.config.ga}', 'auto');
|
||||
ga('send', 'pageview');
|
||||
`}}
|
||||
/>:null}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
}
|
37
docs/package.json
Normal file
37
docs/package.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "graphene-docs",
|
||||
"version": "1.0.0",
|
||||
"description": "Graphene docs",
|
||||
"main": "n/a",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "npm install && ./node_modules/.bin/gatsby build",
|
||||
"deploy": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
"graphene"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"codemirror": "5.9.0",
|
||||
"copy-webpack-plugin": "^0.2.0",
|
||||
"es6-promise": "^3.0.2",
|
||||
"extract-text-webpack-plugin": "^0.9.1",
|
||||
"gatsby": "^0.7.7",
|
||||
"graphiql": "^0.4.2",
|
||||
"graphql": "^0.4.13",
|
||||
"jeet": "^6.1.2",
|
||||
"lodash": "^3.10.1",
|
||||
"nib": "^1.1.0",
|
||||
"react": "^0.14.6",
|
||||
"radium": "0.14.2",
|
||||
"react-burger-menu": "^1.4.12",
|
||||
"react-document-title": "^2.0.1",
|
||||
"react-dom": "^0.14.6",
|
||||
"react-router": "^0.13.5",
|
||||
"rupture": "^0.6.1",
|
||||
"stylus-loader": "^1.4.2",
|
||||
"url-loader": "^0.5.7",
|
||||
"webpack": "^1.12.9"
|
||||
}
|
||||
}
|
9
docs/pages/_empty.js
Normal file
9
docs/pages/_empty.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import React from 'react';
|
||||
|
||||
class Empty extends React.Component {
|
||||
render() {
|
||||
return <div />;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Empty;
|
156
docs/pages/_header.js
Normal file
156
docs/pages/_header.js
Normal file
|
@ -0,0 +1,156 @@
|
|||
var IN_BROWSER = typeof window != 'undefined';
|
||||
import React from 'react';
|
||||
var browser_supported;
|
||||
if (IN_BROWSER) {
|
||||
var isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 &&
|
||||
navigator.userAgent && !navigator.userAgent.match('CriOS');
|
||||
var browser_supported = !isSafari;
|
||||
}
|
||||
if (IN_BROWSER && browser_supported) {
|
||||
var glfx = require('../vendor/glfx.optim')
|
||||
var particlesJS = require('../vendor/particles.js')
|
||||
}
|
||||
|
||||
class Header extends React.Component {
|
||||
update() {
|
||||
if (!this.mounted) return;
|
||||
var w = this.video.width;
|
||||
var h = this.video.height;
|
||||
if (window.devicePixelRatio > 1) {
|
||||
w = w/2;
|
||||
h = h/2;
|
||||
}
|
||||
this.texture._.initFromCanvas(w, h, this.video);
|
||||
|
||||
this.canvas.draw(this.texture).tiltShift(0, h*2/3, 10, h*2/3, 15, 200).update();
|
||||
requestAnimationFrame(this.update.bind(this));
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.mounted = false;
|
||||
}
|
||||
componentDidMount() {
|
||||
if (!(IN_BROWSER && browser_supported)) return;
|
||||
this.mounted = true;
|
||||
new particlesJS('header-background', {
|
||||
"particles": {
|
||||
"number": {
|
||||
"value": 40,
|
||||
"density": {
|
||||
"enable": true,
|
||||
"value_area": 800
|
||||
}
|
||||
},
|
||||
"color": {
|
||||
"value": "#ffffff"
|
||||
},
|
||||
"shape": {
|
||||
"type": "circle",
|
||||
"stroke": {
|
||||
"width": 0,
|
||||
"color": "#000000"
|
||||
},
|
||||
"polygon": {
|
||||
"nb_sides": 8
|
||||
}
|
||||
},
|
||||
"opacity": {
|
||||
"value": 1,
|
||||
"random": true,
|
||||
"anim": {
|
||||
"enable": true,
|
||||
"speed": .1,
|
||||
"opacity_min": .1,
|
||||
"sync": true
|
||||
}
|
||||
},
|
||||
"size": {
|
||||
"value": 2.2,
|
||||
"random": false,
|
||||
"anim": {
|
||||
"enable": false,
|
||||
"speed": .2,
|
||||
"size_min": 2.44,
|
||||
"sync": true
|
||||
}
|
||||
},
|
||||
"line_linked": {
|
||||
"enable": true,
|
||||
"distance": 240,
|
||||
"color": "#ffffff",
|
||||
"opacity": .2,
|
||||
"width": 3
|
||||
},
|
||||
"move": {
|
||||
"enable": true,
|
||||
"speed": 1.1,
|
||||
"direction": "none",
|
||||
"random": true,
|
||||
"straight": false,
|
||||
"out_mode": "bounce",
|
||||
"bounce": false,
|
||||
"attract": {
|
||||
"enable": false,
|
||||
"rotateX": 600,
|
||||
"rotateY": 600
|
||||
}
|
||||
}
|
||||
},
|
||||
"interactivity": {
|
||||
"detect_on": "canvas",
|
||||
"events": {
|
||||
"onhover": {
|
||||
"enable": true,
|
||||
"mode": "repulse"
|
||||
},
|
||||
"onclick": {
|
||||
"enable": true,
|
||||
"mode": "repulse"
|
||||
},
|
||||
"resize": true
|
||||
},
|
||||
"modes": {
|
||||
"grab": {
|
||||
"distance": 400,
|
||||
"line_linked": {
|
||||
"opacity": 1
|
||||
}
|
||||
},
|
||||
"bubble": {
|
||||
"distance": 250,
|
||||
"size": 0,
|
||||
"duration": 2,
|
||||
"opacity": 0,
|
||||
"speed": 3
|
||||
},
|
||||
"repulse": {
|
||||
"distance": 73.08694910712113,
|
||||
"duration": 0.4
|
||||
},
|
||||
"push": {
|
||||
"particles_nb": 4
|
||||
},
|
||||
"remove": {
|
||||
"particles_nb": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"retina_detect": true
|
||||
});
|
||||
var header_background = document.getElementById('header-background');
|
||||
this.canvas = glfx.canvas();
|
||||
this.video = header_background.children[0];
|
||||
this.context = window.pJSDom[0].pJS.canvas.ctx;
|
||||
this.texture = this.canvas.texture(this.video);
|
||||
header_background.appendChild(this.canvas);
|
||||
requestAnimationFrame(this.update.bind(this));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="header-background">
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Header;
|
51
docs/pages/_template.js
Normal file
51
docs/pages/_template.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
import React from 'react';
|
||||
import { RouteHandler, Link, State } from 'react-router';
|
||||
import Icon from 'assets/icon'
|
||||
import {stack as Menu} from 'react-burger-menu';
|
||||
|
||||
import Header from './_header'
|
||||
import logo from '!raw!assets/logo.svg'
|
||||
import styles from '../css/main.styl'
|
||||
|
||||
class Template extends React.Component {
|
||||
render() {
|
||||
var path = this.props.page.path;
|
||||
var isIndex = path == '/';
|
||||
return (
|
||||
<div>
|
||||
<Menu width={200} right>
|
||||
<span><Link to="/playground/" className={path.indexOf('/playground')==0?"active":null}>Try it out</Link></span>
|
||||
<span><Link to="/docs/quickstart/" className={path.indexOf('/docs')==0?"active":null}>Docs</Link></span>
|
||||
<span><Link to="/community/">Community</Link></span>
|
||||
<a href="https://github.com/graphql-python/graphene/">Github</a>
|
||||
</Menu>
|
||||
<header className="header">
|
||||
<div className="header-wrapper">
|
||||
<Link className="header-logo" to="/">
|
||||
<Icon src={logo} />
|
||||
Graphene
|
||||
</Link>
|
||||
<nav className="header-nav">
|
||||
<Link to="/playground/" className={path.indexOf('/playground')==0?"active":null}>Try it out</Link>
|
||||
<Link to="/docs/quickstart/" className={path.indexOf('/docs')==0?"active":null}>Docs</Link>
|
||||
<Link to="/community/">Community</Link>
|
||||
<a href="https://github.com/graphql-python/graphene/">Github</a>
|
||||
</nav>
|
||||
</div>
|
||||
{isIndex?
|
||||
<div className="header-extended">
|
||||
<h1>
|
||||
GraphQL in Python<br />
|
||||
made <strong>simple</strong>
|
||||
</h1>
|
||||
<Link to="/docs/quickstart/" className="get-started">Get Started</Link>
|
||||
<Header />
|
||||
</div>:null}
|
||||
</header>
|
||||
<RouteHandler {...this.props}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Template;
|
39
docs/pages/community.md
Normal file
39
docs/pages/community.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
layout: page
|
||||
title: Community
|
||||
active_tab: community
|
||||
description: The biggest GraphQL Community in Python
|
||||
---
|
||||
|
||||
Graphene is constantly developing thanks to an active volunteer community. There are many different places where you discuss Graphene, share your experiences or ask for help. **Your feedback and participation are very welcome**!
|
||||
|
||||
If you think working with Graphene is fun, there are many ways you can contribute to it. Please join us in the Slack community and help us shape the next generation API’s.
|
||||
|
||||
[![Public Slack Discussion](https://graphql-slack.herokuapp.com/badge.svg)](https://graphql-slack.herokuapp.com/)
|
||||
|
||||
## Our Repositories
|
||||
|
||||
- **GraphQL Core**: [Source Code] - [PyPI package]
|
||||
- **GraphQL Relay**: [Source Code][1] - [PyPI package][2]
|
||||
- **Graphene**: [Source Code][3] - [PyPI package][4]
|
||||
|
||||
Django integration:
|
||||
- **graphql-django-view**: [Source Code][5] - [PyPI package][6]
|
||||
- **django-graphiql**: [Source Code][7] - [PyPI package][8]
|
||||
|
||||
## Other related projects
|
||||
|
||||
- [Flask GraphQL Demo](https://github.com/amitsaha/flask-graphql-demo) by [@echorand](https://twitter.com/echorand)
|
||||
- [Example GraphQL application](https://github.com/msoedov/flask-graphql-example) by [@msoedov](https://twitter.com/msoedov) with Flask, pypy/python3 and MongoDB (using Docker containers)
|
||||
|
||||
|
||||
[Source Code]: https://github.com/graphql-python/graphql-core
|
||||
[PyPI package]: https://pypi.python.org/pypi/graphql-core
|
||||
[1]: https://github.com/graphql-python/graphql-relay
|
||||
[2]: https://pypi.python.org/pypi/graphql-relay
|
||||
[3]: https://github.com/graphql-python/graphene
|
||||
[4]: https://pypi.python.org/pypi/graphene
|
||||
[5]: https://github.com/graphql-python/graphql-django-view
|
||||
[6]: https://pypi.python.org/pypi/graphql-django-view
|
||||
[7]: https://github.com/graphql-python/django-graphiql
|
||||
[8]: https://pypi.python.org/pypi/django-graphiql
|
59
docs/pages/docs/_template.js
Normal file
59
docs/pages/docs/_template.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
import React from 'react';
|
||||
import { RouteHandler, Link, State } from 'react-router';
|
||||
import _ from 'lodash';
|
||||
|
||||
class Template extends React.Component {
|
||||
goToPage(event) {
|
||||
event.target.blur();
|
||||
var page = event.target.value;
|
||||
this.context.router.transitionTo(page);
|
||||
}
|
||||
render() {
|
||||
var docs = this.props.config.docs;
|
||||
var docs_index = _.indexBy(this.props.pages, 'path');
|
||||
var pages = [];
|
||||
var aside_links = Object.keys(docs).map((key) => {
|
||||
let group = docs[key];
|
||||
return <div className="docs-aside-group" key={key}>
|
||||
<h3>{group.name}</h3>
|
||||
{group.pages.map((page) => {
|
||||
pages.push(page)
|
||||
return <Link key={page} to={page}>{docs_index[page].data.title}</Link>
|
||||
})}
|
||||
</div>;
|
||||
});
|
||||
var aside_options = Object.keys(docs).map((key) => {
|
||||
let group = docs[key];
|
||||
return <optgroup key={key} label={group.name}>
|
||||
{group.pages.map((page) => {
|
||||
return <option key={page} value={page}>{docs_index[page].data.title}</option>
|
||||
})}
|
||||
</optgroup>;
|
||||
});
|
||||
var next_page_index = pages.indexOf(this.props.page.path)+1;
|
||||
var next_page = pages[next_page_index];
|
||||
return (
|
||||
<div>
|
||||
<div className="page-title"><h1>Documentation</h1></div>
|
||||
<div className="docs">
|
||||
<aside className="docs-aside">
|
||||
{aside_links}
|
||||
<select className="docs-aside-navselect" value={this.props.page.path} onChange={this.goToPage.bind(this)}>
|
||||
{aside_options}
|
||||
</select>
|
||||
</aside>
|
||||
<div className="docs-content">
|
||||
<RouteHandler {...this.props} docs={true}/>
|
||||
{next_page?<Link className="docs-next" to={next_page}>Next - {docs_index[next_page].data.title} →</Link>:null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Template.contextTypes = {
|
||||
router: React.PropTypes.func
|
||||
};
|
||||
|
||||
module.exports = Template;
|
102
docs/pages/docs/basic-types.md
Normal file
102
docs/pages/docs/basic-types.md
Normal file
|
@ -0,0 +1,102 @@
|
|||
---
|
||||
title: Basic Types
|
||||
description: Walkthrough Basic Types
|
||||
---
|
||||
|
||||
# Basic Types
|
||||
|
||||
Graphene define the following base Scalar Types:
|
||||
- `graphene.String`
|
||||
- `graphene.Int`
|
||||
- `graphene.Float`
|
||||
- `graphene.Boolean`
|
||||
- `graphene.ID`
|
||||
|
||||
Also the following Types are available:
|
||||
- `graphene.List`
|
||||
- `graphene.NonNull`
|
||||
|
||||
## Shorcuts
|
||||
|
||||
There are some shorcuts for code easier.
|
||||
The following are equivalent
|
||||
|
||||
```python
|
||||
# A list of strings
|
||||
string_list = graphene.List(graphene.String())
|
||||
string_list = graphene.String().List
|
||||
|
||||
# A non-null string
|
||||
string_non_null = graphene.String().NonNull
|
||||
string_non_null = graphene.NonNull(graphene.String())
|
||||
```
|
||||
|
||||
|
||||
## Custom scalars
|
||||
|
||||
You can also create a custom scalar for your schema.
|
||||
If you want to create a DateTime Scalar Type just type:
|
||||
|
||||
```python
|
||||
import datetime
|
||||
from graphql.core.language import ast
|
||||
|
||||
class DateTime(Scalar):
|
||||
'''DateTime'''
|
||||
@staticmethod
|
||||
def serialize(dt):
|
||||
return dt.isoformat()
|
||||
|
||||
@staticmethod
|
||||
def parse_literal(node):
|
||||
if isinstance(node, ast.StringValue):
|
||||
return datetime.datetime.strptime(
|
||||
node.value, "%Y-%m-%dT%H:%M:%S.%f")
|
||||
|
||||
@staticmethod
|
||||
def parse_value(value):
|
||||
return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
|
||||
```
|
||||
|
||||
## Mounting in ClassTypes
|
||||
|
||||
This types if are mounted in a `ObjectType`, `Interface` or `Mutation`,
|
||||
would act as `Field`s.
|
||||
|
||||
```python
|
||||
class Person(graphene.ObjectType):
|
||||
name = graphene.String()
|
||||
|
||||
# Is equivalent to:
|
||||
class Person(graphene.ObjectType):
|
||||
name = graphene.Field(graphene.String())
|
||||
```
|
||||
|
||||
## Mounting in Fields
|
||||
|
||||
If the types are mounted in a `Field`, would act as `Argument`s.
|
||||
|
||||
```python
|
||||
graphene.Field(graphene.String(), to=graphene.String())
|
||||
|
||||
# Is equivalent to:
|
||||
graphene.Field(graphene.String(), to=graphene.Argument(graphene.String()))
|
||||
```
|
||||
|
||||
|
||||
## Using custom object types as argument
|
||||
|
||||
To use a custom object type as an argument, you need to inherit `graphene.InputObjectType`, not `graphene.ObjectType`.
|
||||
|
||||
```python
|
||||
class CustomArgumentObjectType(graphene.InputObjectType):
|
||||
field1 = graphene.String()
|
||||
field2 = graphene.String()
|
||||
|
||||
```
|
||||
|
||||
Then, when defining this in an argument, you need to wrap it in an `Argument` object.
|
||||
|
||||
```python
|
||||
graphene.Field(graphene.String(), to=graphene.Argument(CustomArgumentObjectType))
|
||||
```
|
159
docs/pages/docs/django/filtering.md
Normal file
159
docs/pages/docs/django/filtering.md
Normal file
|
@ -0,0 +1,159 @@
|
|||
---
|
||||
title: Filtering
|
||||
description: Details of how to perform filtering in Graphene Django
|
||||
---
|
||||
|
||||
# Filtering
|
||||
|
||||
Graphene integrates with [django-filter](https://django-filter.readthedocs.org)
|
||||
to provide filtering of results. See the
|
||||
[usage documentation](https://django-filter.readthedocs.org/en/latest/usage.html#the-filter)
|
||||
for details on the format for `filter_fields`.
|
||||
|
||||
This filtering is only available when using the Django integrations
|
||||
(i.e. nodes which extend `DjangoNode`). Additionally `django-filter`
|
||||
is an optional dependency of Graphene. You will need to
|
||||
install it manually, which can be done as follows:
|
||||
|
||||
```bash
|
||||
# You'll need to django-filter
|
||||
pip install django-filter
|
||||
```
|
||||
|
||||
**Note: The techniques below are demoed in the
|
||||
[cookbook example app](https://github.com/graphql-python/graphene/tree/master/examples/cookbook_django).**
|
||||
|
||||
## Filterable fields
|
||||
|
||||
The `filter_fields` parameter is used to specify the fields which can be filtered upon.
|
||||
The value specified here is passed directly to `django-filter`, so see the
|
||||
[filtering documentation](https://django-filter.readthedocs.org/en/latest/usage.html#the-filter)
|
||||
for full details on the range of options available.
|
||||
|
||||
For example:
|
||||
|
||||
```python
|
||||
class AnimalNode(DjangoNode):
|
||||
class Meta:
|
||||
# Assume you have an Animal model defined with the following fields
|
||||
model = Animal
|
||||
filter_fields = ['name', 'genus', 'is_domesticated']
|
||||
|
||||
class Query(ObjectType):
|
||||
animal = relay.NodeField(AnimalNode)
|
||||
all_animals = DjangoFilterConnectionField(AnimalNode)
|
||||
```
|
||||
|
||||
You could then perform a query such as:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
# Note that fields names become camelcased
|
||||
allAnimals(genus: "cat", isDomesticated: true) {
|
||||
edges {
|
||||
node {
|
||||
id,
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also make more complex lookup types available:
|
||||
|
||||
```python
|
||||
class AnimalNode(DjangoNode):
|
||||
class Meta:
|
||||
model = Animal
|
||||
# Provide more complex lookup types
|
||||
filter_fields = {
|
||||
'name': ['exact', 'icontains', 'istartswith'],
|
||||
'genus': ['exact'],
|
||||
'is_domesticated': ['exact'],
|
||||
}
|
||||
```
|
||||
|
||||
Which you could query as follows:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
# Note that fields names become camelcased
|
||||
allAnimals(name_Icontains: "lion") {
|
||||
edges {
|
||||
node {
|
||||
id,
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Orderable fields
|
||||
|
||||
Ordering can also be specified using `filter_order_by`. Like `filter_fields`,
|
||||
this value is also passed directly to `django-filter` as the `order_by` field.
|
||||
For full details see the
|
||||
[order_by documentation](https://django-filter.readthedocs.org/en/latest/usage.html#ordering-using-order-by).
|
||||
|
||||
For example:
|
||||
|
||||
```python
|
||||
class AnimalNode(DjangoNode):
|
||||
class Meta:
|
||||
model = Animal
|
||||
filter_fields = ['name', 'genus', 'is_domesticated']
|
||||
# Either a tuple/list of fields upon which ordering is allowed, or
|
||||
# True to allow filtering on all fields specified in filter_fields
|
||||
order_by_fields = True
|
||||
```
|
||||
|
||||
You can then control the ordering via the `orderBy` argument:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
allAnimals(orderBy: "name") {
|
||||
edges {
|
||||
node {
|
||||
id,
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Custom Filtersets
|
||||
|
||||
By default Graphene provides easy access to the most commonly used
|
||||
features of `django-filter`. This is done by transparently creating a
|
||||
`django_filters.FilterSet` class for you and passing in the values for
|
||||
`filter_fields` and `order_by_fields`.
|
||||
|
||||
However, you may find this to be insufficient. In these cases you can
|
||||
create your own `Filterset` as follows:
|
||||
|
||||
```python
|
||||
class AnimalNode(DjangoNode):
|
||||
class Meta:
|
||||
# Assume you have an Animal model defined with the following fields
|
||||
model = Animal
|
||||
filter_fields = ['name', 'genus', 'is_domesticated']
|
||||
|
||||
|
||||
class AnimalFilter(django_filters.FilterSet):
|
||||
# Do case-insensitive lookups on 'name'
|
||||
name = django_filters.CharFilter(lookup_type='iexact')
|
||||
|
||||
class Meta:
|
||||
model = Animal
|
||||
fields = ['name', 'genus', 'is_domesticated']
|
||||
|
||||
|
||||
class Query(ObjectType):
|
||||
animal = relay.NodeField(AnimalNode)
|
||||
# We specify our custom AnimalFilter using the filterset_class param
|
||||
all_animals = DjangoFilterConnectionField(AnimalNode,
|
||||
filterset_class=AnimalFilter)
|
||||
```
|
293
docs/pages/docs/django/tutorial.md
Normal file
293
docs/pages/docs/django/tutorial.md
Normal file
|
@ -0,0 +1,293 @@
|
|||
---
|
||||
title: Quickstart
|
||||
description: A Quick guide to Graphene in Django
|
||||
---
|
||||
|
||||
# Django Tutorial
|
||||
|
||||
Graphene has a number of additional features that are designed to make
|
||||
working with Django *really simple*.
|
||||
|
||||
**Note: The code in this quickstart is pulled from the
|
||||
[cookbook example app](https://github.com/graphql-python/graphene/tree/master/examples/cookbook_django)**.
|
||||
|
||||
|
||||
## Setup the Django project
|
||||
|
||||
We will setup the project, create the following:
|
||||
|
||||
* A Django project called `cookbook`
|
||||
* An app within `cookbook` called `ingredients`
|
||||
|
||||
```bash
|
||||
# Create the project directory
|
||||
mkdir cookbook
|
||||
cd cookbook
|
||||
|
||||
# Create a virtualenv to isolate our package dependencies locally
|
||||
virtualenv env
|
||||
source env/bin/activate # On Windows use `env\Scripts\activate`
|
||||
|
||||
# Install Django and Graphene with Django support
|
||||
pip install django
|
||||
pip install graphene[django]
|
||||
pip install django-graphiql
|
||||
|
||||
# Set up a new project with a single application
|
||||
django-admin.py startproject cookbook . # Note the trailing '.' character
|
||||
django-admin.py startapp ingredients
|
||||
```
|
||||
|
||||
Now sync your database for the first time:
|
||||
|
||||
```bash
|
||||
python manage.py migrate
|
||||
```
|
||||
|
||||
Let's create a few simple models...
|
||||
|
||||
|
||||
## Defining our models
|
||||
|
||||
Let's get started with these models:
|
||||
|
||||
```python
|
||||
# cookbook/ingredients/models.py
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Ingredient(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
notes = models.TextField()
|
||||
category = models.ForeignKey(Category, related_name='ingredients')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
```
|
||||
|
||||
## Schema
|
||||
|
||||
GraphQL presents your objects to the world as a graph structure rather than a more
|
||||
hierarchical structure to which you may be accustomed. In order to create this
|
||||
representation, Graphene needs to know about each *type* of object which will appear in
|
||||
the graph.
|
||||
|
||||
This graph also has a *root type* through which all access begins. This is the `Query` class below.
|
||||
In this example, we provide the ability to list all users via `all_users`, and the
|
||||
ability to obtain a specific user via `get_user`.
|
||||
|
||||
Create `cookbook/ingredients/schema.py` and type the following:
|
||||
|
||||
```python
|
||||
# cookbook/ingredients/schema.py
|
||||
from graphene import relay, ObjectType
|
||||
from graphene.contrib.django.filter import DjangoFilterConnectionField
|
||||
from graphene.contrib.django.types import DjangoNode
|
||||
|
||||
from cookbook.ingredients.models import Category, Ingredient
|
||||
|
||||
|
||||
# Graphene will automatically map the User model's fields onto the UserType.
|
||||
# This is configured in the UserType's Meta class (as you can see below)
|
||||
class CategoryNode(DjangoNode):
|
||||
class Meta:
|
||||
model = Category
|
||||
filter_fields = ['name', 'ingredients']
|
||||
filter_order_by = ['name']
|
||||
|
||||
|
||||
class IngredientNode(DjangoNode):
|
||||
class Meta:
|
||||
model = Ingredient
|
||||
# Allow for some more advanced filtering here
|
||||
filter_fields = {
|
||||
'name': ['exact', 'icontains', 'istartswith'],
|
||||
'notes': ['exact', 'icontains'],
|
||||
'category': ['exact'],
|
||||
'category__name': ['exact'],
|
||||
}
|
||||
filter_order_by = ['name', 'category__name']
|
||||
|
||||
|
||||
class Query(ObjectType):
|
||||
category = relay.NodeField(CategoryNode)
|
||||
all_categories = DjangoFilterConnectionField(CategoryNode)
|
||||
|
||||
ingredient = relay.NodeField(IngredientNode)
|
||||
all_ingredients = DjangoFilterConnectionField(IngredientNode)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
```
|
||||
|
||||
The filtering functionality is provided by
|
||||
[django-filter](https://django-filter.readthedocs.org). See the
|
||||
[usage documentation](https://django-filter.readthedocs.org/en/latest/usage.html#the-filter)
|
||||
for details on the format for `filter_fields`.
|
||||
|
||||
Note that the above `Query` class is marked as 'abstract'. This is because we
|
||||
want will now create a project-level query which will combine all our app-level
|
||||
queries.
|
||||
|
||||
Create the parent project-level `cookbook/schema.py`:
|
||||
|
||||
```python
|
||||
import graphene
|
||||
|
||||
import cookbook.ingredients.schema
|
||||
|
||||
|
||||
class Query(cookbook.ingredients.schema.Query):
|
||||
# This class will inherit from multiple Queries
|
||||
# as we begin to add more apps to our project
|
||||
pass
|
||||
|
||||
schema = graphene.Schema(name='Cookbook Schema')
|
||||
schema.query = Query
|
||||
```
|
||||
|
||||
You can think of this as being something like your top-level `urls.py`
|
||||
file (although it currently lacks any namespacing).
|
||||
|
||||
## Adding GraphiQL
|
||||
|
||||
GraphiQL is a web-based integrated development environment to assist in the
|
||||
writing and executing of GraphQL queries. It will provide us with a simple
|
||||
and easy way of testing our cookbook project.
|
||||
|
||||
Add `django_graphiql` to `INSTALLED_APPS` in `cookbook/settings.py`:
|
||||
|
||||
```python
|
||||
INSTALLED_APPS = [
|
||||
...
|
||||
'django_graphiql',
|
||||
|
||||
# This will also make the `graphql_schema` management command available
|
||||
'graphene.contrib.django',
|
||||
]
|
||||
```
|
||||
|
||||
## Creating GraphQL and GraphiQL views
|
||||
|
||||
Unlike a RESTful API, there is only a single URL from which GraphQL is accessed.
|
||||
Requests to this URL are handled by Graphene's `GraphQLView` view.
|
||||
|
||||
Additionally, we'll add a URL for aforementioned GraphiQL, and for the Django admin
|
||||
interface (the latter can be useful for creating test data).
|
||||
|
||||
```python
|
||||
from django.conf.urls import url, include
|
||||
from django.contrib import admin
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from graphene.contrib.django.views import GraphQLView
|
||||
|
||||
from cookbook.schema import schema
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^graphql', csrf_exempt(GraphQLView.as_view(schema=schema))),
|
||||
url(r'^graphiql', include('django_graphiql.urls')),
|
||||
]
|
||||
```
|
||||
|
||||
## Load some test data
|
||||
|
||||
Now is a good time to load up some test data. The easiest option will be to
|
||||
[download the ingredients.json](https://raw.githubusercontent.com/graphql-python/graphene/feature/django/examples/cookbook/cookbook/ingredients/fixtures/ingredients.json)
|
||||
fixture and place it in
|
||||
`cookbook/ingredients/fixtures/ingredients.json`. You can then run the following:
|
||||
|
||||
```
|
||||
$ python ./manage.py loaddata ingredients
|
||||
|
||||
Installed 6 object(s) from 1 fixture(s)
|
||||
```
|
||||
|
||||
Alternatively you can use the Django admin interface to create some data youself.
|
||||
You'll need to run the development server (see below), and create a login
|
||||
for yourself too (`./manage.py createsuperuser`).
|
||||
|
||||
## Testing our GraphQL schema
|
||||
|
||||
We're now ready to test the API we've built. Let's fire up the server from the command line.
|
||||
|
||||
```bash
|
||||
$ python ./manage.py runserver
|
||||
|
||||
Performing system checks...
|
||||
Django version 1.9, using settings 'cookbook.settings'
|
||||
Starting development server at http://127.0.0.1:8000/
|
||||
Quit the server with CONTROL-C.
|
||||
```
|
||||
|
||||
Go to [localhost:8000/graphiql](http://localhost:8000/graphiql) and type your first query!
|
||||
|
||||
```graphql
|
||||
query {
|
||||
allIngredients {
|
||||
edges {
|
||||
node {
|
||||
id,
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The above will return the names & IDs for all ingredients. But perhaps you want
|
||||
a specific ingredient:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
# Graphene creates globally unique IDs for all objects.
|
||||
# You may need to copy this value from the results of the first query
|
||||
ingredient(id: "SW5ncmVkaWVudE5vZGU6MQ==") {
|
||||
name
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also get each ingredient for each category:
|
||||
|
||||
```graphql
|
||||
query {
|
||||
allCategories {
|
||||
edges {
|
||||
node {
|
||||
name,
|
||||
ingredients {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or you can get only 'meat' ingredients containing the letter 'e':
|
||||
|
||||
```graphql
|
||||
query {
|
||||
# You can also use `category: "CATEGORY GLOBAL ID"`
|
||||
allIngredients(name_Icontains: "e", categoryName: "Meat") {
|
||||
edges {
|
||||
node {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
53
docs/pages/docs/interfaces.md
Normal file
53
docs/pages/docs/interfaces.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
title: Interfaces
|
||||
description: Walkthrough Interfaces
|
||||
---
|
||||
|
||||
# Interfaces
|
||||
|
||||
An Interface contains the essential fields that will be shared among multiple ObjectTypes.
|
||||
|
||||
The basics:
|
||||
- Each Interface is a Python class that inherits from `graphene.Interface`.
|
||||
- Each attribute of the Interface represents a GraphQL field.
|
||||
|
||||
## Quick example
|
||||
|
||||
This example model defines a Character, which has a name. `Human` and `Droid` inherit from it.
|
||||
|
||||
```python
|
||||
import graphene
|
||||
|
||||
# Character is an Interface
|
||||
class Character(graphene.Interface):
|
||||
name = graphene.String()
|
||||
|
||||
# Human is an ObjectType, as inherits an interface
|
||||
class Human(Character):
|
||||
born_in = graphene.String()
|
||||
|
||||
# Droid is an ObjectType, as inherits an interface
|
||||
class Droid(Character):
|
||||
function = graphene.String()
|
||||
|
||||
```
|
||||
|
||||
**name** is a field in the `Character` interface that will be in both `Human` and `Droid` ObjectTypes (as those inherit from `Character`). Each ObjectType also define extra fields at the same time.
|
||||
|
||||
The above types would have the following representation in a schema:
|
||||
|
||||
```graphql
|
||||
interface Character {
|
||||
name: String
|
||||
}
|
||||
|
||||
type Droid implements Character {
|
||||
name: String
|
||||
function: String
|
||||
}
|
||||
|
||||
type Human implements Character {
|
||||
name: String
|
||||
bornIn: String
|
||||
}
|
||||
```
|
50
docs/pages/docs/introspection-schema.md
Normal file
50
docs/pages/docs/introspection-schema.md
Normal file
|
@ -0,0 +1,50 @@
|
|||
---
|
||||
title: Introspection Schema
|
||||
description: A guide to instrospection schema in Django
|
||||
---
|
||||
|
||||
# Introspection Schema
|
||||
|
||||
Relay uses [Babel Relay Plugin](https://facebook.github.io/relay/docs/guides-babel-plugin.html)
|
||||
that requires you to provide your GraphQL schema data.
|
||||
|
||||
Graphene comes with a management command for Django to dump your schema data to
|
||||
`schema.json` that is compatible with babel-relay-plugin.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Include `graphene.django.contrib` to `INSTALLED_APPS` in you project settings:
|
||||
|
||||
```python
|
||||
INSTALLED_APPS += ('graphene.django.contrib')
|
||||
```
|
||||
|
||||
Assuming your Graphene schema is at `tutorial.quickstart.schema`, run the command:
|
||||
|
||||
```bash
|
||||
./manage.py graphql_schema --schema tutorial.quickstart.schema --out schema.json
|
||||
```
|
||||
|
||||
It dumps your full introspection schema to `schema.json` inside your project root
|
||||
directory. Point `babel-relay-plugin` to this file and you're ready to use Relay
|
||||
with Graphene GraphQL implementation.
|
||||
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
To simplify the command to `./manage.py graphql_schema`, you can specify the
|
||||
parameters in your settings.py:
|
||||
|
||||
```python
|
||||
GRAPHENE_SCHEMA = 'tutorial.quickstart.schema'
|
||||
GRAPHENE_SCHEMA_OUTPUT = 'data/schema.json' # defaults to schema.json
|
||||
```
|
||||
|
||||
Running `./manage.py graphql_schema` dumps your schema to
|
||||
`<project root>/data/schema.json`.
|
||||
|
||||
|
||||
## Help
|
||||
|
||||
Run `./manage.py graphql_schema -h` for command usage.
|
75
docs/pages/docs/mutations.md
Normal file
75
docs/pages/docs/mutations.md
Normal file
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
title: Mutations
|
||||
description: Walkthrough Mutations
|
||||
---
|
||||
|
||||
# Mutations
|
||||
|
||||
A Mutation is a special ObjectType that define also an Input.
|
||||
|
||||
## Quick example
|
||||
|
||||
This example defines a Mutation:
|
||||
|
||||
```python
|
||||
import graphene
|
||||
|
||||
class CreatePerson(graphene.Mutation):
|
||||
class Input:
|
||||
name = graphene.String()
|
||||
|
||||
ok = graphene.String()
|
||||
person = graphene.Field('Person')
|
||||
|
||||
@classmethod
|
||||
def mutate(cls, instance, args, info):
|
||||
person = Person(name=args.get('name'))
|
||||
ok = True
|
||||
return CreatePerson(person=person, ok=ok)
|
||||
```
|
||||
|
||||
**person** and **ok** are the output fields of the Mutation when is resolved.
|
||||
|
||||
**Input** attributes are the arguments that the Mutation `CreatePerson` needs for resolving, in this case **name** will be the only argument for the mutation.
|
||||
|
||||
**mutate** is the function that will be applied once the mutation is called.
|
||||
|
||||
So, we can finish our schema like this:
|
||||
|
||||
```python
|
||||
# ... the Mutation Class
|
||||
|
||||
class Person(graphene.ObjectType):
|
||||
name = graphene.String()
|
||||
|
||||
class MyMutations(graphene.ObjectType):
|
||||
create_person = graphene.Field(CreatePerson)
|
||||
|
||||
schema = graphene.Schema(mutation=MyMutations)
|
||||
```
|
||||
|
||||
## Executing the Mutation
|
||||
|
||||
Then, if we query (`schema.execute(query_str)`) the following:
|
||||
```graphql
|
||||
mutation myFirstMutation {
|
||||
createPerson(name:"Peter") {
|
||||
person {
|
||||
name
|
||||
}
|
||||
ok
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We should receive:
|
||||
|
||||
```json
|
||||
{
|
||||
"createPerson": {
|
||||
"person" : {
|
||||
name: "Peter"
|
||||
},
|
||||
"ok": true
|
||||
}
|
||||
}
|
49
docs/pages/docs/objecttypes.md
Normal file
49
docs/pages/docs/objecttypes.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
title: ObjectTypes
|
||||
description: Walkthrough ObjectTypes
|
||||
---
|
||||
|
||||
# ObjectTypes
|
||||
|
||||
An ObjectType is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you’re querying.
|
||||
|
||||
The basics:
|
||||
- Each ObjectType is a Python class that inherits `graphene.ObjectType` or inherits an implemented [Interface](/docs/interfaces/).
|
||||
- Each attribute of the ObjectType represents a GraphQL field.
|
||||
|
||||
## Quick example
|
||||
|
||||
This example model defines a Person, which has a first_name and last_name:
|
||||
|
||||
```python
|
||||
import graphene
|
||||
|
||||
class Person(graphene.ObjectType):
|
||||
first_name = graphene.String()
|
||||
last_name = graphene.String()
|
||||
full_name = graphene.String()
|
||||
|
||||
def resolve_full_name(self, args, info):
|
||||
return '{} {}'.format(self.first_name, self.last_name)
|
||||
```
|
||||
|
||||
**first_name** and **last_name** are fields of the ObjectType. Each field is specified as a class attribute, and each attribute maps to a GraphQL field.
|
||||
|
||||
The above `Person` ObjectType would have the following representation in a schema:
|
||||
|
||||
```graphql
|
||||
type Person {
|
||||
firstName: String
|
||||
lastName: String
|
||||
fullName: String
|
||||
}
|
||||
```
|
||||
|
||||
## Instances as containers
|
||||
|
||||
Graphene `ObjectType`s could act as containers too.
|
||||
So with the previous example you could do.
|
||||
|
||||
```python
|
||||
peter = Person(first_name='Peter', last_name='Griffin')
|
||||
```
|
52
docs/pages/docs/quickstart.md
Normal file
52
docs/pages/docs/quickstart.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
title: Getting started
|
||||
description: A Quick guide to Graphene
|
||||
---
|
||||
|
||||
# Getting started
|
||||
|
||||
Let's build a basic GraphQL schema from scratch.
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python (2.6.5+, 2.7, 3.2, 3.3, 3.4, 3.5, pypy)
|
||||
- Graphene (0.4+)
|
||||
|
||||
|
||||
## Project setup
|
||||
|
||||
```bash
|
||||
pip install graphene
|
||||
```
|
||||
|
||||
## Creating a basic Schema
|
||||
|
||||
A GraphQL schema describes your data model, and provides a GraphQL server with an associated set of resolve methods that know how to fetch data.
|
||||
|
||||
We are going to create a very simple schema, with a `Query` with only one field: `hello`. And when we query it should return `"World"`.
|
||||
|
||||
|
||||
```python
|
||||
import graphene
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
hello = graphene.String()
|
||||
|
||||
def resolve_hello(self, args, info):
|
||||
return 'World'
|
||||
|
||||
schema = graphene.Schema(query=Query)
|
||||
```
|
||||
|
||||
|
||||
## Querying
|
||||
|
||||
Then, we can start querying our schema:
|
||||
|
||||
```python
|
||||
result = schema.execute('{ hello }')
|
||||
print result.data['hello'] # "World"
|
||||
```
|
||||
|
||||
Congrats! You got your first graphene schema working!
|
63
docs/pages/docs/relay.md
Normal file
63
docs/pages/docs/relay.md
Normal file
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
title: Relay
|
||||
description: A Relay implementation in Graphene
|
||||
---
|
||||
|
||||
# Relay
|
||||
|
||||
Graphene has complete support for [Relay](https://facebook.github.io/relay/docs/graphql-relay-specification.html) and offers some utils to make integration from Python easy.
|
||||
|
||||
## Nodes
|
||||
|
||||
A `Node` is an Interface provided by `graphene.relay` that contains a single field `id` (which is a `ID!`). Any object that inherits from it have to implement a `get_node` method for retrieving a `Node` by an *id*.
|
||||
|
||||
Example usage (taken from the [Starwars Relay example](https://github.com/graphql-python/graphene/blob/master/examples/starwars_relay/schema.py)):
|
||||
|
||||
```python
|
||||
class Ship(relay.Node):
|
||||
'''A ship in the Star Wars saga'''
|
||||
name = graphene.String(description='The name of the ship.')
|
||||
|
||||
@classmethod
|
||||
def get_node(cls, id, info):
|
||||
return get_ship(id)
|
||||
```
|
||||
|
||||
The `id` returned by the `Ship` type when you query it will be a scalar which contains the enough info for the server for knowing it's type and it's id.
|
||||
|
||||
For example, the instance `Ship(id=1)` will return `U2hpcDox` as the id when you query it (which is the base64 encoding of `Ship:1`), and which could be useful later if we want to query a node by its id.
|
||||
|
||||
|
||||
## Connection
|
||||
|
||||
A connection is a vitaminized version of a List that provides ways of slicing and paginating through it. The way you create Connection fields in `graphene` is using `relay.ConnectionField`.
|
||||
|
||||
You can create connection fields in any ObjectType, but the connection **must** be linked to an object which inherits from `Node` (in this case, a `Ship`).
|
||||
|
||||
```python
|
||||
class Faction(graphene.ObjectType):
|
||||
name = graphene.String()
|
||||
ships = relay.ConnectionField(Ship)
|
||||
|
||||
def resolve_ships(self, args, info):
|
||||
return []
|
||||
```
|
||||
|
||||
## Node Root field
|
||||
|
||||
As is required in the [Relay specification](https://facebook.github.io/relay/graphql/objectidentification.htm#sec-Node-root-field), the server must implement a root field called `node` that returns a `Node` Interface.
|
||||
|
||||
For this reason, `graphene` provides the field `relay.NodeField`, which links to any type in the Schema which inherits from `Node`. Example usage:
|
||||
|
||||
```python
|
||||
class Query(graphene.ObjectType):
|
||||
node = relay.NodeField()
|
||||
```
|
||||
|
||||
|
||||
## Useful links
|
||||
|
||||
* [Getting started with Relay](https://facebook.github.io/relay/docs/graphql-relay-specification.html)
|
||||
* [Relay Global Identification Specification](https://facebook.github.io/relay/graphql/objectidentification.htm)
|
||||
* [Relay Cursor Connection Specification](https://facebook.github.io/relay/graphql/connections.htm)
|
||||
* [Relay input Object Mutation](https://facebook.github.io/relay/graphql/mutations.htm)
|
10
docs/pages/index.md
Normal file
10
docs/pages/index.md
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
path: /
|
||||
---
|
||||
<div class="starwars-example-wrapper"><a class="starwars-example" href="http://swapi.graphene-python.org/">Check our Django Starwars API example!</a></div>
|
||||
|
||||
## Meet Graphene
|
||||
|
||||
Graphene is a Python library for building GraphQL schemas/types fast and easily.
|
||||
|
||||
**But, what is GraphQL?** A GraphQL query is a string interpreted by a server that returns data in a specified format. We believe *GraphQL* is the next big thing after peanut butter and *REST*.
|
13
docs/pages/playground.js
Normal file
13
docs/pages/playground.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
import DocumentTitle from 'react-document-title';
|
||||
import PlaygroundWrapper from 'playground-wrapper';
|
||||
|
||||
class Playground extends React.Component {
|
||||
render() {
|
||||
return <DocumentTitle title="Playground - Graphene">
|
||||
<PlaygroundWrapper />
|
||||
</DocumentTitle>;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Playground;
|
9
docs/patched-react.js
Normal file
9
docs/patched-react.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
// React patched version for render in server side always with same ids
|
||||
if (typeof window === "undefined") {
|
||||
var ServerReactRootIndex = require('react/lib/ServerReactRootIndex');
|
||||
ServerReactRootIndex.createReactRootIndex = function(){
|
||||
return "graphene";
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = require('original-react');
|
234
docs/playground/GraphenePlayground.js
Normal file
234
docs/playground/GraphenePlayground.js
Normal file
|
@ -0,0 +1,234 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { RouteHandler, Link, State } from 'react-router';
|
||||
import CodeMirror from 'codemirror';
|
||||
import { graphql } from 'graphql';
|
||||
import GraphiQL from 'graphiql';
|
||||
import schema from './schema';
|
||||
import pypyjs_vm from 'pypyjs';
|
||||
|
||||
import 'codemirror/mode/python/python';
|
||||
import 'codemirror/addon/lint/lint';
|
||||
import '../css/playground.styl';
|
||||
|
||||
if (typeof PUBLIC_PATH === "undefined") {
|
||||
var PUBLIC_PATH = '';
|
||||
}
|
||||
|
||||
pypyjs_vm.rootURL = `${PUBLIC_PATH}/playground/lib/`;
|
||||
pypyjs_vm.cacheKey = 'graphene';
|
||||
|
||||
CodeMirror.registerHelper('lint', 'python', function (text, options, editor) {
|
||||
return (options.errors || []).map((error) => {
|
||||
var tokens = editor.getLineTokens(error.line - 1);
|
||||
tokens = tokens.filter((token, pos) => {
|
||||
return !!token.type || token.string.trim().length > 0;
|
||||
});
|
||||
if (!tokens) return [];
|
||||
return {
|
||||
message: `${error.name}: ${error.message}`,
|
||||
severity: 'error',
|
||||
type: 'syntax',
|
||||
from: CodeMirror.Pos(error.line - 1, tokens[0].start),
|
||||
to: CodeMirror.Pos(error.line - 1, tokens[tokens.length-1].end),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
function graphQLFetcher(graphQLParams) {
|
||||
return graphql(schema, graphQLParams.query);
|
||||
}
|
||||
|
||||
var default_interpreter;
|
||||
class Playground extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {pypyjs: false, stdout: '', response:''};
|
||||
}
|
||||
stdout() {
|
||||
console.log('stdout', arguments);
|
||||
}
|
||||
componentDidMount() {
|
||||
if (default_interpreter) {
|
||||
this.pypy_interpreter = default_interpreter;
|
||||
this.pypy_interpreter.stdout = this.stdout.bind(this);
|
||||
}
|
||||
else {
|
||||
this.pypy_interpreter = new pypyjs_vm({
|
||||
stdin: function(){},
|
||||
stdout: this.stdout.bind(this),
|
||||
stderr: function(){},
|
||||
rootURL: `${PUBLIC_PATH}/playground/lib/`
|
||||
});
|
||||
default_interpreter = this.pypy_interpreter;
|
||||
}
|
||||
|
||||
this.pypyjs = this.pypy_interpreter.ready().then(() => {
|
||||
return this.pypy_interpreter.exec(`
|
||||
import graphene
|
||||
import js
|
||||
from collections import OrderedDict
|
||||
from graphql.core.execution.executor import Executor
|
||||
from graphql.core.execution.middlewares.sync import SynchronousExecutionMiddleware
|
||||
from graphql.core.error import GraphQLError, format_error
|
||||
|
||||
def get_wrapped(f):
|
||||
if hasattr(f, 'func_closure') and f.func_closure:
|
||||
return get_wrapped(f.func_closure[0].cell_contents)
|
||||
return f
|
||||
|
||||
class TrackResolver(SynchronousExecutionMiddleware):
|
||||
@staticmethod
|
||||
def run_resolve_fn(resolver, original_resolver):
|
||||
if resolver.func.__module__ == '__main__':
|
||||
line = get_wrapped(resolver.func).resolver.func_code.co_firstlineno
|
||||
js.globals.markLine(line-3)
|
||||
return SynchronousExecutionMiddleware.run_resolve_fn(resolver, original_resolver)
|
||||
|
||||
__graphene_executor = Executor([TrackResolver()], map_type=OrderedDict)
|
||||
`);
|
||||
}).then(() => {
|
||||
this.createSchema(this.props.initialSchema);
|
||||
}).then(() => {
|
||||
this.setState({pypyjs: true, response:'"Execute the query for see the results"'});
|
||||
});
|
||||
|
||||
window.markLine = (lineNo) => {
|
||||
this.markLine(lineNo);
|
||||
}
|
||||
|
||||
this.editor = CodeMirror(ReactDOM.findDOMNode(this.refs.schemaCode), {
|
||||
value: this.props.initialSchema,
|
||||
mode: "python",
|
||||
theme: "graphene",
|
||||
lineNumbers: true,
|
||||
tabSize: 4,
|
||||
indentUnit: 4,
|
||||
gutters: ["CodeMirror-linenumbers", "breakpoints"],
|
||||
lint: {
|
||||
errors: [],
|
||||
},
|
||||
});
|
||||
this.editor.on("change", this.onEditorChange.bind(this));
|
||||
}
|
||||
onEditorChange() {
|
||||
if (this.changeTimeout) {
|
||||
clearTimeout(this.changeTimeout);
|
||||
}
|
||||
if (this.props.onEditSchema) {
|
||||
var value = this.editor.getValue();
|
||||
if (value != this.props.initialSchema) {
|
||||
this.props.onEditSchema(value)
|
||||
}
|
||||
}
|
||||
|
||||
this.changeTimeout = setTimeout(() =>
|
||||
this.updateSchema()
|
||||
, 300);
|
||||
}
|
||||
updateSchema() {
|
||||
this.createSchema(this.editor.getValue());
|
||||
}
|
||||
createSchema(code) {
|
||||
if (this.previousCode == code) return;
|
||||
console.log('createSchema');
|
||||
this.validSchema = null;
|
||||
this.pypyjs.then(() => {
|
||||
return this.pypy_interpreter.exec(`
|
||||
schema = None
|
||||
${code}
|
||||
assert schema, 'You have to define a schema'
|
||||
`)
|
||||
}).then(() => {
|
||||
console.log('NO ERRORS');
|
||||
this.removeErrors();
|
||||
this.validSchema = true;
|
||||
}, (err) => {
|
||||
this.editor.options.lint.errors = [];
|
||||
console.log('ERRORS', err);
|
||||
this.logError(err);
|
||||
this.validSchema = false;
|
||||
}).then(this.updateGraphiQL.bind(this));
|
||||
this.previousCode = code;
|
||||
}
|
||||
updateGraphiQL() {
|
||||
if (this.validSchema) {
|
||||
this.refs.graphiql.state.schema = null;
|
||||
this.refs.graphiql.componentDidMount();
|
||||
this.refs.graphiql.forceUpdate();
|
||||
this.refs.graphiql.refs.docExplorer.forceUpdate();
|
||||
}
|
||||
}
|
||||
logError(error) {
|
||||
var lines = error.trace.split('\n');
|
||||
var file_errors = lines.map((errorLine) => {
|
||||
return errorLine.match(/File "<string>", line (\d+)/);
|
||||
}).filter((x) => !! x);
|
||||
if (!file_errors.length) return;
|
||||
var line = parseInt(file_errors[file_errors.length-1][1]);
|
||||
error.line = line-2;
|
||||
if (error.name == "ImportError" && error.message == "No module named django") {
|
||||
error.message = "Django is not supported yet in Playground editor";
|
||||
}
|
||||
this.editor.options.lint.errors.push(error);
|
||||
CodeMirror.signal(this.editor, 'change', this.editor);
|
||||
}
|
||||
removeErrors() {
|
||||
this.editor.options.lint.errors = [];
|
||||
CodeMirror.signal(this.editor, 'change', this.editor);
|
||||
}
|
||||
fetcher (graphQLParams) {
|
||||
if (!this.validSchema) {
|
||||
return graphQLFetcher(arguments);
|
||||
}
|
||||
return this.execute(graphQLParams.query);
|
||||
}
|
||||
execute(query) {
|
||||
// console.log('execute', query);
|
||||
return this.pypyjs.then(() => {
|
||||
var x = `
|
||||
import json
|
||||
result = __graphene_executor.execute(schema.schema, '''${query}''')
|
||||
result_dict = {};
|
||||
if result.errors:
|
||||
result_dict['errors'] = [format_error(e) for e in result.errors]
|
||||
if result.data:
|
||||
result_dict['data'] = result.data
|
||||
result_json = json.dumps(result_dict)
|
||||
`;
|
||||
return this.pypy_interpreter.exec(x)
|
||||
}
|
||||
).then(() =>
|
||||
this.pypy_interpreter.get(`result_json`)
|
||||
).then((data) => {
|
||||
var json_data = JSON.parse(data);
|
||||
return json_data;
|
||||
});
|
||||
}
|
||||
markLine(lineNo) {
|
||||
console.log(lineNo);
|
||||
var hlLine = this.editor.addLineClass(lineNo, "text", "activeline");
|
||||
// var mark = this.editor.markText({line: lineNo, ch: 0}, {line: lineNo, ch: 10}, {className: "called-function"});
|
||||
setTimeout(() => {
|
||||
this.editor.removeLineClass(lineNo, "text", "activeline");
|
||||
}, 1200);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className="playground">
|
||||
{!this.state.pypyjs?<div className="loading" />:null}
|
||||
<div className="playground-schema">
|
||||
<header className="playground-schema-header">
|
||||
Schema
|
||||
</header>
|
||||
<div className="playground-schema-editor" ref="schemaCode" />
|
||||
</div>
|
||||
<div className="playground-graphiql">
|
||||
<GraphiQL ref="graphiql" fetcher={this.fetcher.bind(this)} response={this.state.response} onEditQuery={this.props.onEditQuery} query={this.props.initialQuery}/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Playground;
|
4
docs/playground/examples/hello.graphql
Normal file
4
docs/playground/examples/hello.graphql
Normal file
|
@ -0,0 +1,4 @@
|
|||
query {
|
||||
hello
|
||||
ping(to:"Peter")
|
||||
}
|
13
docs/playground/examples/hello.schema.py
Normal file
13
docs/playground/examples/hello.schema.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
import graphene
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
hello = graphene.String()
|
||||
ping = graphene.String(to=graphene.String())
|
||||
|
||||
def resolve_hello(self, args, info):
|
||||
return 'World'
|
||||
|
||||
def resolve_ping(self, args, info):
|
||||
return 'Pinging {}'.format(args.get('to'))
|
||||
|
||||
schema = graphene.Schema(query=Query)
|
20
docs/playground/examples/starwars_relay.graphql
Normal file
20
docs/playground/examples/starwars_relay.graphql
Normal file
|
@ -0,0 +1,20 @@
|
|||
query {
|
||||
empire {
|
||||
id
|
||||
name
|
||||
ships(first:2) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node(id:"U2hpcDo4") {
|
||||
id
|
||||
... on Ship {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
148
docs/playground/examples/starwars_relay.schema.py
Normal file
148
docs/playground/examples/starwars_relay.schema.py
Normal file
|
@ -0,0 +1,148 @@
|
|||
import graphene
|
||||
from graphene import relay, resolve_only_args
|
||||
|
||||
class Ship(relay.Node):
|
||||
'''A ship in the Star Wars saga'''
|
||||
name = graphene.String(description='The name of the ship.')
|
||||
|
||||
@classmethod
|
||||
def get_node(cls, id, info):
|
||||
return get_ship(id)
|
||||
|
||||
class Faction(relay.Node):
|
||||
'''A faction in the Star Wars saga'''
|
||||
name = graphene.String(description='The name of the faction.')
|
||||
ships = relay.ConnectionField(
|
||||
Ship, description='The ships used by the faction.')
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_ships(self, **args):
|
||||
# Transform the instance ship_ids into real instances
|
||||
return [get_ship(ship_id) for ship_id in self.ships]
|
||||
|
||||
@classmethod
|
||||
def get_node(cls, id, info):
|
||||
return get_faction(id)
|
||||
|
||||
class IntroduceShip(relay.ClientIDMutation):
|
||||
class Input:
|
||||
ship_name = graphene.String(required=True)
|
||||
faction_id = graphene.String(required=True)
|
||||
|
||||
ship = graphene.Field(Ship)
|
||||
faction = graphene.Field(Faction)
|
||||
|
||||
@classmethod
|
||||
def mutate_and_get_payload(cls, input, info):
|
||||
ship_name = input.get('ship_name')
|
||||
faction_id = input.get('faction_id')
|
||||
ship = create_ship(ship_name, faction_id)
|
||||
faction = get_faction(faction_id)
|
||||
return IntroduceShip(ship=ship, faction=faction)
|
||||
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
rebels = graphene.Field(Faction)
|
||||
empire = graphene.Field(Faction)
|
||||
node = relay.NodeField()
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_rebels(self):
|
||||
return get_rebels()
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_empire(self):
|
||||
return get_empire()
|
||||
|
||||
|
||||
class Mutation(graphene.ObjectType):
|
||||
introduce_ship = graphene.Field(IntroduceShip)
|
||||
|
||||
schema = graphene.Schema(name='Starwars Relay Schema')
|
||||
schema.query = Query
|
||||
schema.mutation = Mutation
|
||||
|
||||
xwing = Ship(
|
||||
id='1',
|
||||
name='X-Wing',
|
||||
)
|
||||
ywing = Ship(
|
||||
id='2',
|
||||
name='Y-Wing',
|
||||
)
|
||||
awing = Ship(
|
||||
id='3',
|
||||
name='A-Wing',
|
||||
)
|
||||
|
||||
# Yeah, technically it's Corellian. But it flew in the service of the rebels,
|
||||
# so for the purposes of this demo it's a rebel ship.
|
||||
falcon = Ship(
|
||||
id='4',
|
||||
name='Millenium Falcon',
|
||||
)
|
||||
homeOne = Ship(
|
||||
id='5',
|
||||
name='Home One',
|
||||
)
|
||||
tieFighter = Ship(
|
||||
id='6',
|
||||
name='TIE Fighter',
|
||||
)
|
||||
tieInterceptor = Ship(
|
||||
id='7',
|
||||
name='TIE Interceptor',
|
||||
)
|
||||
executor = Ship(
|
||||
id='8',
|
||||
name='Executor',
|
||||
)
|
||||
rebels = Faction(
|
||||
id='1',
|
||||
name='Alliance to Restore the Republic',
|
||||
ships=['1', '2', '3', '4', '5']
|
||||
)
|
||||
empire = Faction(
|
||||
id='2',
|
||||
name='Galactic Empire',
|
||||
ships=['6', '7', '8']
|
||||
)
|
||||
data = {
|
||||
'Faction': {
|
||||
'1': rebels,
|
||||
'2': empire
|
||||
},
|
||||
'Ship': {
|
||||
'1': xwing,
|
||||
'2': ywing,
|
||||
'3': awing,
|
||||
'4': falcon,
|
||||
'5': homeOne,
|
||||
'6': tieFighter,
|
||||
'7': tieInterceptor,
|
||||
'8': executor
|
||||
}
|
||||
}
|
||||
|
||||
def create_ship(ship_name, faction_id):
|
||||
from .schema import Ship
|
||||
next_ship = len(data['Ship'].keys()) + 1
|
||||
new_ship = Ship(
|
||||
id=str(next_ship),
|
||||
name=ship_name
|
||||
)
|
||||
data['Ship'][new_ship.id] = new_ship
|
||||
data['Faction'][faction_id].ships.append(new_ship.id)
|
||||
return new_ship
|
||||
|
||||
def get_ship(_id):
|
||||
return data['Ship'][_id]
|
||||
|
||||
def get_faction(_id):
|
||||
return data['Faction'][_id]
|
||||
|
||||
def get_rebels():
|
||||
return get_faction('1')
|
||||
|
||||
def get_empire():
|
||||
return get_faction('2')
|
8
docs/playground/examples/tea_store.graphql
Normal file
8
docs/playground/examples/tea_store.graphql
Normal file
|
@ -0,0 +1,8 @@
|
|||
query {
|
||||
store {
|
||||
teas(orderBy:"name") {
|
||||
name
|
||||
steepingTime
|
||||
}
|
||||
}
|
||||
}
|
38
docs/playground/examples/tea_store.schema.py
Normal file
38
docs/playground/examples/tea_store.schema.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
import graphene
|
||||
|
||||
class Tea(graphene.ObjectType):
|
||||
name = graphene.String()
|
||||
steeping_time = graphene.Int()
|
||||
|
||||
TEAS = [
|
||||
Tea(name='Earl Grey Blue Star', steeping_time=5),
|
||||
Tea(name='Milk Oolong', steeping_time=3),
|
||||
Tea(name='Gunpowder Golden Temple', steeping_time=3),
|
||||
Tea(name='Assam Hatimara', steeping_time=5),
|
||||
Tea(name='Bancha', steeping_time=2),
|
||||
Tea(name='Ceylon New Vithanakande', steeping_time=5),
|
||||
Tea(name='Golden Tip Yunnan', steeping_time=5),
|
||||
Tea(name='Jasmine Phoenix Pearls', steeping_time=3),
|
||||
Tea(name='Kenya Milima', steeping_time=5),
|
||||
Tea(name='Pu Erh First Grade', steeping_time=4),
|
||||
Tea(name='Sencha Makoto', steeping_time=3),
|
||||
]
|
||||
|
||||
class Store(graphene.ObjectType):
|
||||
teas = graphene.List(Tea, order_by=graphene.String())
|
||||
|
||||
def resolve_teas(self, args, info):
|
||||
order_by = args.get("order_by")
|
||||
if order_by == "steepingTime":
|
||||
return sorted(self.teas, key=lambda tea: tea.steeping_time)
|
||||
elif order_by == "name":
|
||||
return sorted(self.teas, key=lambda tea: tea.name)
|
||||
return self.teas
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
store = graphene.Field(Store)
|
||||
|
||||
def resolve_store(self, args, info):
|
||||
return Store(teas=TEAS)
|
||||
|
||||
schema = graphene.Schema(query=Query)
|
1
docs/playground/graphene-js/.gitignore
vendored
Normal file
1
docs/playground/graphene-js/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
pypy-release-nojit
|
107
docs/playground/graphene-js/FunctionPromise.js
Normal file
107
docs/playground/graphene-js/FunctionPromise.js
Normal file
|
@ -0,0 +1,107 @@
|
|||
//
|
||||
// FunctionPromise: possibly-asynchronous function constructor.
|
||||
//
|
||||
// This is a prototype polyfill for a FunctionPromise object as described in:
|
||||
//
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=854627
|
||||
//
|
||||
// Where possible it will arrange for the function body to be parsed/compiled
|
||||
// off of the main thread, with the function object returned asynchronously
|
||||
// via a promise. The fallback implementation processes just falls back to
|
||||
// the standard synchronous Function() constructor.
|
||||
//
|
||||
// It doesn't (yet) have the following features from the linked proposal:
|
||||
//
|
||||
// * ability to copy to different workers
|
||||
// * ability to store in IndexedDB
|
||||
//
|
||||
function FunctionPromise(/* [args1[, args2[, ...argN]],], functionBody) */) {
|
||||
|
||||
var useFallback =
|
||||
typeof window === "undefined" ||
|
||||
window.FunctionPromise !== FunctionPromise ||
|
||||
typeof document === "undefined" ||
|
||||
typeof document.createElement === "undefined" ||
|
||||
typeof document.head === "undefined" ||
|
||||
typeof document.head.appendChild === "undefined" ||
|
||||
typeof Blob === "undefined" ||
|
||||
typeof URL === "undefined" ||
|
||||
typeof URL.createObjectURL === "undefined";
|
||||
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
|
||||
// For the fallback case, we just use the normal Function constructor.
|
||||
|
||||
if (useFallback) {
|
||||
try {
|
||||
var fn = Function.apply(null, args);
|
||||
return Promise.resolve(fn);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have all the necessary pieces, we can do this asynchronously
|
||||
// by writing a <script> tag into the DOM.
|
||||
|
||||
var funcid = FunctionPromise._nextid++;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
try {
|
||||
var funcSrc = [];
|
||||
funcSrc.push("window.FunctionPromise._results[" + funcid + "]=");
|
||||
funcSrc.push("function(");
|
||||
if (args.length > 1) {
|
||||
funcSrc.push(args[0]);
|
||||
for (var i = 1; i < args.length - 1; i++) {
|
||||
funcSrc.push(",");
|
||||
funcSrc.push(args[i]);
|
||||
}
|
||||
}
|
||||
funcSrc.push("){");
|
||||
funcSrc.push(args[args.length - 1]);
|
||||
funcSrc.push("}");
|
||||
var dataUrl = URL.createObjectURL(new Blob(funcSrc));
|
||||
var scriptTag = document.createElement("script");
|
||||
var cleanup = function() {
|
||||
URL.revokeObjectURL(dataUrl);
|
||||
scriptTag.remove();
|
||||
delete window.FunctionPromise._results[funcid];
|
||||
}
|
||||
scriptTag.onerror = function() {
|
||||
reject(new Error("unknown error loading FunctionPromise"))
|
||||
cleanup();
|
||||
}
|
||||
scriptTag.onload = function() {
|
||||
if (window.FunctionPromise._results[funcid]) {
|
||||
resolve(window.FunctionPromise._results[funcid]);
|
||||
} else {
|
||||
// No function, something must have gone wrong.
|
||||
// Likely a syntax error in the function body string.
|
||||
// Fall back to Function() constructor to surface it.
|
||||
try {
|
||||
Function.apply(null, args);
|
||||
reject(new Error("unknown error fulfilling FunctionPromise"));
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
cleanup();
|
||||
}
|
||||
scriptTag.src = dataUrl;
|
||||
document.head.appendChild(scriptTag);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
FunctionPromise._nextid = 0;
|
||||
FunctionPromise._results = {};
|
||||
|
||||
if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
|
||||
if (typeof Promise === "undefined") {
|
||||
Promise = require('es6-promise').Promise;
|
||||
}
|
||||
module.exports = FunctionPromise;
|
||||
}
|
37
docs/playground/graphene-js/build.sh
Executable file
37
docs/playground/graphene-js/build.sh
Executable file
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
if [ ! -d pypyjs-release-nojit ] ; then
|
||||
git clone https://github.com/pypyjs/pypyjs-release-nojit.git
|
||||
fi
|
||||
|
||||
GRAPHENE_DIR="$(python -c "import os; import graphene; print os.path.dirname(graphene.__file__)")"
|
||||
GRAPHQL_DIR="$(python -c "import os; import graphql; print os.path.dirname(graphql.__file__)")"
|
||||
GRAPHQL_RELAY_DIR="$(python -c "import os; import graphql_relay; print os.path.dirname(graphql_relay.__file__)")"
|
||||
SIX_DIR="$(python -c "import os; import six; print six.__file__.rstrip('c')")"
|
||||
|
||||
cd pypyjs-release-nojit
|
||||
|
||||
eval python tools/module_bundler.py add ./lib/modules "$GRAPHENE_DIR"
|
||||
eval python tools/module_bundler.py add ./lib/modules "$GRAPHQL_DIR"
|
||||
eval python tools/module_bundler.py add ./lib/modules "$GRAPHQL_RELAY_DIR"
|
||||
eval python tools/module_bundler.py add ./lib/modules "$SIX_DIR"
|
||||
|
||||
python ./tools/module_bundler.py preload ./lib/modules graphene
|
||||
python ./tools/module_bundler.py preload ./lib/modules graphene.relay
|
||||
python ./tools/module_bundler.py preload ./lib/modules graphql
|
||||
python ./tools/module_bundler.py preload ./lib/modules graphql_relay
|
||||
python ./tools/module_bundler.py preload ./lib/modules six
|
||||
|
||||
python ./tools/module_bundler.py remove ./lib/modules unittest
|
||||
|
||||
lib_dirname=`perl -e 'use Cwd "abs_path";print abs_path(shift)' lib/`
|
||||
|
||||
if [ -d ../../../static/playground/lib ] ; then
|
||||
rm ../../../static/playground/lib
|
||||
fi
|
||||
|
||||
mkdir -p ../../../static/playground
|
||||
|
||||
exec ln -s "$lib_dirname/" ../../../static/playground/lib
|
976
docs/playground/graphene-js/pypyjs.js
Normal file
976
docs/playground/graphene-js/pypyjs.js
Normal file
|
@ -0,0 +1,976 @@
|
|||
//
|
||||
// pypyjs: an experimental in-browser python environment.
|
||||
//
|
||||
|
||||
(function() {
|
||||
|
||||
// Expose the main pypyjs function at global scope for this file,
|
||||
// as well as in any module exports or 'window' object we can find.
|
||||
if (this) {
|
||||
this.pypyjs = pypyjs;
|
||||
}
|
||||
if (typeof window !== "undefined") {
|
||||
window.pypyjs = pypyjs;
|
||||
}
|
||||
if (typeof module !== "undefined") {
|
||||
if (typeof module.exports !== "undefined") {
|
||||
module.exports = pypyjs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Generic debugging printf.
|
||||
var debug = function(){};
|
||||
if (typeof console !== "undefined") {
|
||||
debug = console.log.bind(console);
|
||||
} else if (typeof print !== "undefined" && typeof window === "undefined") {
|
||||
debug = print;
|
||||
}
|
||||
|
||||
|
||||
// Find the directory containing this very file.
|
||||
// It can be quite difficult depending on execution environment...
|
||||
if (typeof __dirname === "undefined" || true) {
|
||||
var __dirname = "./";
|
||||
// A little hackery to find the URL of this very file.
|
||||
// Throw an error, then parse the stack trace looking for filenames.
|
||||
var errlines = (new Error()).stack.split("\n");
|
||||
for (var i = 0; i < errlines.length; i++) {
|
||||
var match = /(at Anonymous function \(|at |@)(.+\/)pypyjs.js/.exec(errlines[i]);
|
||||
if (match) {
|
||||
__dirname = match[2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (__dirname.charAt(__dirname.length - 1) !== "/") {
|
||||
__dirname += "/";
|
||||
}
|
||||
|
||||
|
||||
if (typeof Promise === "undefined") {
|
||||
var Promise = require('es6-promise').Promise;
|
||||
}
|
||||
|
||||
|
||||
// Ensure we have reference to a 'FunctionPromise' constructor.
|
||||
var FunctionPromise = require("./FunctionPromise.js");
|
||||
|
||||
if (typeof FunctionPromise === "undefined") {
|
||||
throw "FunctionPromise object not found";
|
||||
}
|
||||
|
||||
|
||||
// Create functions for handling default stdio streams.
|
||||
// These will be shared by all VM instances by default.
|
||||
//
|
||||
// We default stdout and stderr to process outputs if available,
|
||||
// printing/logging functions otherwise, and /dev/null if nothing
|
||||
// else is available. Unfortunately there's no good way to read
|
||||
// synchronously from stdin in javascript, so that's always /dev/null.
|
||||
|
||||
var devNull = {
|
||||
stdin: function() { return null; },
|
||||
stdout: function() { },
|
||||
stderr: function() { }
|
||||
}
|
||||
|
||||
var stdio = {
|
||||
stdin: null,
|
||||
stdout: null,
|
||||
stderr: null
|
||||
}
|
||||
|
||||
stdio.stdin = devNull.stdin;
|
||||
|
||||
if (typeof process !== "undefined") {
|
||||
if (typeof process.stdout !== "undefined") {
|
||||
stdio.stdout = function(x) { process.stdout.write(x); }
|
||||
}
|
||||
if (typeof process.stderr !== "undefined") {
|
||||
stdio.stderr = function(x) { process.stderr.write(x); }
|
||||
}
|
||||
}
|
||||
|
||||
var _print, _printErr;
|
||||
if (typeof window === "undefined") {
|
||||
// print, printErr from v8, spidermonkey
|
||||
if (typeof print !== "undefined") {
|
||||
_print = print;
|
||||
}
|
||||
if (typeof printErr !== "undefined") {
|
||||
_printErr = printErr;
|
||||
}
|
||||
}
|
||||
if (typeof console !== "undefined") {
|
||||
if (typeof _print === "undefined") {
|
||||
_print = console.log.bind(console);
|
||||
}
|
||||
if (typeof _printErr === "undefined") {
|
||||
_printErr = console.error.bind(console);
|
||||
}
|
||||
}
|
||||
|
||||
if (stdio.stdout == null && typeof _print !== "undefined") {
|
||||
// print()/console.log() will add a newline, so we buffer until we
|
||||
// receive one and then let it add it for us.
|
||||
stdio.stdout = (function() {
|
||||
var buffer = [];
|
||||
return function(data) {
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var x = data.charAt(i);
|
||||
if (x !== "\n") {
|
||||
buffer.push(x);
|
||||
} else {
|
||||
_print(buffer.join(""));
|
||||
buffer.splice(undefined, buffer.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
if (stdio.stderr == null && typeof _printErr !== "undefined") {
|
||||
// printErr()/console.error() will add a newline, so we buffer until we
|
||||
// receive one and then let it add it for us.
|
||||
stdio.stderr = (function() {
|
||||
var buffer = [];
|
||||
return function(data) {
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var x = data.charAt(i);
|
||||
if (x !== "\n") {
|
||||
buffer.push(x);
|
||||
} else {
|
||||
_printErr(buffer.join(""));
|
||||
buffer.splice(undefined, buffer.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
if (stdio.stdout === null) {
|
||||
stdio.stdout = devNull.stdout;
|
||||
}
|
||||
|
||||
if (stdio.stderr === null) {
|
||||
stdio.stderr = devNull.stderr;
|
||||
}
|
||||
|
||||
function pypyjs(opts) {
|
||||
|
||||
opts = opts || {};
|
||||
this.rootURL = opts.rootURL;
|
||||
this.totalMemory = opts.totalMemory || 128 * 1024 * 1024;
|
||||
this.autoLoadModules = opts.autoLoadModules || true;
|
||||
this._pendingModules = {};
|
||||
this._loadedModules = {};
|
||||
this._allModules = {};
|
||||
|
||||
// Allow opts to override default IO streams.
|
||||
this.stdin = opts.stdin || stdio.stdin;
|
||||
this.stdout = opts.stdout || stdio.stdout;
|
||||
this.stderr = opts.stderr || stdio.stderr;
|
||||
|
||||
// Default to finding files relative to this very file.
|
||||
if (!this.rootURL && !pypyjs.rootURL) {
|
||||
pypyjs.rootURL = __dirname;
|
||||
}
|
||||
if (this.rootURL && this.rootURL.charAt(this.rootURL.length - 1) !== "/") {
|
||||
this.rootURL += "/";
|
||||
}
|
||||
|
||||
// If we haven't already done so, fetch and load the code for the VM.
|
||||
// We do this once and cache the result for re-use, so that we don't
|
||||
// have to pay asmjs compilation overhead each time we create the VM.
|
||||
|
||||
if (! pypyjs._vmBuilderPromise) {
|
||||
pypyjs._vmBuilderPromise = this.fetch("pypyjs.vm.js").then((function(xhr) {
|
||||
// Parse the compiled code, hopefully asynchronously.
|
||||
// Unfortunately our use of Function constructor here doesn't
|
||||
// play very well with nodejs, where things like 'module' and
|
||||
// 'require' are not in the global scope. We have to pass them
|
||||
// in explicitly as arguments.
|
||||
var funcBody = [
|
||||
// This is the compiled code for the VM.
|
||||
xhr.responseText,
|
||||
'\n',
|
||||
// Ensure that some functions are available on the Module,
|
||||
// for linking with jitted code.
|
||||
'if (!Module._jitInvoke && typeof _jitInvoke !== "undefined") {',
|
||||
' Module._jitInvoke = _jitInvoke;',
|
||||
'}',
|
||||
// Keep some functions that are not exported by default, but
|
||||
// which appear in this scope when evaluating the above.
|
||||
"Module._emjs_make_handle = _emjs_make_handle;",
|
||||
"Module._emjs_free = _emjs_free;",
|
||||
// Call dependenciesFulfilled if it won't be done automatically.
|
||||
"dependenciesFulfilled=function() { inDependenciesFulfilled(FS); };",
|
||||
"if(!memoryInitializer||(!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER))dependenciesFulfilled();",
|
||||
].join("");
|
||||
return FunctionPromise("Module", "inDependenciesFulfilled", "require",
|
||||
"module", "__filename", "__dirname", funcBody)
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
// Create a new instance of the compiled VM, bound to local state
|
||||
// and a local Module object.
|
||||
|
||||
this._ready = new Promise((function(resolve, reject) {
|
||||
|
||||
// Initialize the Module object.
|
||||
// We make it available on this object so that we can use
|
||||
// its methods to execute code in the VM.
|
||||
var Module = {};
|
||||
this._module = Module;
|
||||
Module.TOTAL_MEMORY = this.totalMemory;
|
||||
|
||||
// We will set up the filesystem manually when we're ready.
|
||||
Module.noFSInit = true;
|
||||
Module.thisProgram = "/lib/pypyjs/pypyjs.js";
|
||||
Module.filePackagePrefixURL = this.rootURL || pypyjs.rootURL;
|
||||
Module.memoryInitializerPrefixURL = this.rootURL || pypyjs.rootURL;
|
||||
Module.locateFile = function(name) {
|
||||
return (this.rootURL || pypyjs.rootURL) + name;
|
||||
}
|
||||
|
||||
// Don't start or stop the program, just set it up.
|
||||
// We'll call the API functions ourself.
|
||||
Module.noInitialRun = true;
|
||||
Module.noExitRuntime = true;
|
||||
|
||||
// Route stdin to an overridable method on the object.
|
||||
var stdin = (function stdin() {
|
||||
return this.stdin();
|
||||
}).bind(this);
|
||||
|
||||
// Route stdout to an overridable method on the object.
|
||||
// We buffer the output for efficiency.
|
||||
var stdout_buffer = []
|
||||
var stdout = (function stdout(x) {
|
||||
var c = String.fromCharCode(x);
|
||||
stdout_buffer.push(c);
|
||||
if (c === "\n" || stdout_buffer.length >= 128) {
|
||||
this.stdout(stdout_buffer.join(""));
|
||||
stdout_buffer = [];
|
||||
}
|
||||
}).bind(this);
|
||||
|
||||
// Route stderr to an overridable method on the object.
|
||||
// We do not buffer stderr.
|
||||
var stderr = (function stderr(x) {
|
||||
var c = String.fromCharCode(x);
|
||||
this.stderr(c);
|
||||
}).bind(this);
|
||||
|
||||
// This is where execution will continue after loading
|
||||
// the memory initialization data, if any.
|
||||
var initializedResolve, initializedReject;
|
||||
var initializedP = new Promise(function(resolve, reject) {
|
||||
initializedResolve = resolve;
|
||||
initializedReject = reject;
|
||||
});
|
||||
var FS;
|
||||
var dependenciesFulfilled = function(fs) {
|
||||
FS = fs;
|
||||
// Initialize the filesystem state.
|
||||
try {
|
||||
FS.init(stdin, stdout, stderr);
|
||||
Module.FS_createPath("/", "lib/pypyjs/lib_pypy", true, false);
|
||||
Module.FS_createPath("/", "lib/pypyjs/lib-python/2.7", true, false);
|
||||
initializedResolve();
|
||||
} catch (err) {
|
||||
initializedReject(err);
|
||||
}
|
||||
}
|
||||
|
||||
// Begin fetching the metadata for available python modules.
|
||||
// With luck these can download while we jank around compiling
|
||||
// all of that javascript.
|
||||
// XXX TODO: also load memory initializer this way.
|
||||
var moduleDataP = this.fetch("modules/index.json");
|
||||
|
||||
pypyjs._vmBuilderPromise.then((function(vmBuilder) {
|
||||
var args = [
|
||||
Module,
|
||||
dependenciesFulfilled,
|
||||
typeof undefined,
|
||||
typeof undefined,
|
||||
typeof undefined,
|
||||
typeof __dirname
|
||||
];
|
||||
// This links the async-compiled module into our Module object.
|
||||
vmBuilder.apply(null, args);
|
||||
return initializedP;
|
||||
}).bind(this)).then((function() {
|
||||
// Continue with processing the downloaded module metadata.
|
||||
return moduleDataP.then((function(xhr) {
|
||||
// Store the module index, and load any preload modules.
|
||||
var modIndex = JSON.parse(xhr.responseText);
|
||||
this._allModules = modIndex.modules;
|
||||
if (modIndex.preload) {
|
||||
for (var name in modIndex.preload) {
|
||||
this._writeModuleFile(name, modIndex.preload[name]);
|
||||
}
|
||||
}
|
||||
// It's finally safe to launch the VM.
|
||||
Module.run();
|
||||
Module._rpython_startup_code();
|
||||
var pypy_home = Module.intArrayFromString("/lib/pypyjs/pypyjs.js");
|
||||
pypy_home = Module.allocate(pypy_home, 'i8', Module.ALLOC_NORMAL);
|
||||
Module._pypy_setup_home(pypy_home, 0);
|
||||
Module._free(pypy_home);
|
||||
var initCode = [
|
||||
"import js",
|
||||
"import sys; sys.platform = 'js'",
|
||||
"import traceback",
|
||||
"top_level_scope = {'__name__': '__main__'}"
|
||||
];
|
||||
initCode.forEach(function(codeStr) {
|
||||
var code = Module.intArrayFromString(codeStr);
|
||||
var code = Module.allocate(code, 'i8', Module.ALLOC_NORMAL);
|
||||
if (!code) {
|
||||
throw new pypyjs.Error('Failed to allocate memory');
|
||||
}
|
||||
var res = Module._pypy_execute_source(code);
|
||||
if (res < 0) {
|
||||
throw new pypyjs.Error('Failed to execute python code');
|
||||
}
|
||||
Module._free(code);
|
||||
});
|
||||
}).bind(this))
|
||||
}).bind(this))
|
||||
.then(resolve, reject);
|
||||
}).bind(this));
|
||||
|
||||
};
|
||||
|
||||
|
||||
// A simple file-fetching wrapper around XMLHttpRequest,
|
||||
// that treats paths as relative to the pypyjs.js root url.
|
||||
//
|
||||
pypyjs.prototype.fetch = function (relpath, responseType) {
|
||||
if (typeof window === "undefined") {
|
||||
var localStorage = false;
|
||||
}
|
||||
else {
|
||||
var localStorage = window.localStorage;
|
||||
}
|
||||
var use_cache = pypyjs.cacheKey && localStorage && relpath != "pypyjs.vm.js";
|
||||
if (use_cache) {
|
||||
var item = localStorage.getItem(pypyjs.cacheKey+':'+relpath);
|
||||
if (item) {
|
||||
return new Promise((function(resolve, reject) {
|
||||
resolve({ responseText: item });
|
||||
}))
|
||||
}
|
||||
}
|
||||
// For the web, use XMLHttpRequest.
|
||||
if (typeof XMLHttpRequest !== "undefined") {
|
||||
return new Promise((function(resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onload = function() {
|
||||
if (xhr.status >= 400) {
|
||||
reject(xhr)
|
||||
} else {
|
||||
if (use_cache && xhr.responseText) {
|
||||
localStorage.setItem(pypyjs.cacheKey+':'+relpath, xhr.responseText);
|
||||
}
|
||||
resolve(xhr);
|
||||
}
|
||||
};
|
||||
var rootURL = this.rootURL || pypyjs.rootURL;
|
||||
xhr.open('GET', rootURL + relpath, true);
|
||||
xhr.responseType = responseType || "text";
|
||||
xhr.send(null);
|
||||
}).bind(this));
|
||||
}
|
||||
// For nodejs, use fs.readFile.
|
||||
if (typeof fs !== "undefined" && typeof fs.readFile !== "undefined") {
|
||||
return new Promise((function(resolve, reject) {
|
||||
var rootURL = this.rootURL || pypyjs.rootURL;
|
||||
fs.readFile(path.join(rootURL, relpath), function(err, data) {
|
||||
if (err) return reject(err);
|
||||
resolve({ responseText: data.toString() });
|
||||
});
|
||||
}).bind(this));
|
||||
}
|
||||
// For spidermonkey, use snarf (which has a binary read mode).
|
||||
if (typeof snarf !== "undefined") {
|
||||
return new Promise((function(resolve, reject) {
|
||||
var rootURL = this.rootURL || pypyjs.rootURL;
|
||||
var data = snarf(rootURL + relpath);
|
||||
resolve({ responseText: data });
|
||||
}).bind(this));
|
||||
}
|
||||
// For d8, use read() and readbuffer().
|
||||
if (typeof read !== "undefined" && typeof readbuffer !== "undefined") {
|
||||
return new Promise((function(resolve, reject) {
|
||||
var rootURL = this.rootURL || pypyjs.rootURL;
|
||||
var data = read(rootURL + relpath);
|
||||
resolve({ responseText: data });
|
||||
}).bind(this));
|
||||
}
|
||||
return new Promise(function(resolve, reject) {
|
||||
reject("unable to fetch files");
|
||||
});
|
||||
};
|
||||
|
||||
if (typeof localStorage !== "undefined") {
|
||||
var localStorage = false;
|
||||
}
|
||||
|
||||
// pypyjs.prototype.fetch = function fetch(relpath, responseType) {
|
||||
// // For the web, use XMLHttpRequest.
|
||||
// var use_cache = pypyjs.cacheKey && localStorage;
|
||||
// if (use_cache) {
|
||||
// if (var item = localStorage.getItem(pypyjs.cacheKey+'-'+relpath)) {
|
||||
// resolve({ responseText: item });
|
||||
// }
|
||||
// }
|
||||
// if (typeof XMLHttpRequest !== "undefined") {
|
||||
// return new Promise((function(resolve, reject) {
|
||||
// var xhr = new XMLHttpRequest();
|
||||
// xhr.onload = function() {
|
||||
// if (xhr.status >= 400) {
|
||||
// reject(xhr)
|
||||
// } else {
|
||||
// console.log(xhr.responseText);
|
||||
// if (use_cache && xhr.responseText) {
|
||||
// localStorage.setItem(pypyjs.cacheKey+'-'+relpath, xhr.responseText);
|
||||
// }
|
||||
// resolve(xhr);
|
||||
// }
|
||||
// };
|
||||
// var rootURL = this.rootURL || pypyjs.rootURL;
|
||||
// xhr.open('GET', rootURL + relpath, true);
|
||||
// xhr.responseType = responseType || "text";
|
||||
// xhr.send(null);
|
||||
// }).bind(this));
|
||||
// }
|
||||
// // For nodejs, use fs.readFile.
|
||||
// if (typeof fs !== "undefined" && typeof fs.readFile !== "undefined") {
|
||||
// return new Promise((function(resolve, reject) {
|
||||
// var rootURL = this.rootURL || pypyjs.rootURL;
|
||||
// fs.readFile(path.join(rootURL, relpath), function(err, data) {
|
||||
// if (err) return reject(err);
|
||||
// resolve({ responseText: data.toString() });
|
||||
// });
|
||||
// }).bind(this));
|
||||
// }
|
||||
// // For spidermonkey, use snarf (which has a binary read mode).
|
||||
// if (typeof snarf !== "undefined") {
|
||||
// return new Promise((function(resolve, reject) {
|
||||
// var rootURL = this.rootURL || pypyjs.rootURL;
|
||||
// var data = snarf(rootURL + relpath);
|
||||
// resolve({ responseText: data });
|
||||
// }).bind(this));
|
||||
// }
|
||||
// // For d8, use read() and readbuffer().
|
||||
// if (typeof read !== "undefined" && typeof readbuffer !== "undefined") {
|
||||
// return new Promise((function(resolve, reject) {
|
||||
// var rootURL = this.rootURL || pypyjs.rootURL;
|
||||
// var data = read(rootURL + relpath);
|
||||
// resolve({ responseText: data });
|
||||
// }).bind(this));
|
||||
// }
|
||||
// return new Promise(function(resolve, reject) {
|
||||
// reject("unable to fetch files");
|
||||
// });
|
||||
// };
|
||||
|
||||
|
||||
// Method to execute python source directly in the VM.
|
||||
//
|
||||
// This is the basic way to push code into the pypyjs VM.
|
||||
// Calling code should not use it directly; rather we use it
|
||||
// as a primitive to build up a nicer execution API.
|
||||
//
|
||||
pypyjs.prototype._execute_source = function _execute_source(code) {
|
||||
var Module = this._module;
|
||||
code = "try:\n" +
|
||||
" " + code + "\n" +
|
||||
"except Exception:\n" +
|
||||
" typ, val, tb = sys.exc_info()\n" +
|
||||
" err_name = getattr(typ, '__name__', str(typ))\n" +
|
||||
" err_msg = str(val)\n" +
|
||||
" err_trace = traceback.format_exception(typ, val, tb)\n" +
|
||||
" err_trace = ''.join(err_trace)\n" +
|
||||
" js.globals['pypyjs']._lastErrorName = err_name\n" +
|
||||
" js.globals['pypyjs']._lastErrorMessage = err_msg\n" +
|
||||
" js.globals['pypyjs']._lastErrorTrace = err_trace\n";
|
||||
var code_chars = Module.intArrayFromString(code);
|
||||
var code_ptr = Module.allocate(code_chars, 'i8', Module.ALLOC_NORMAL);
|
||||
if (!code_ptr) {
|
||||
return Promise.reject(new pypyjs.Error("Failed to allocate memory"));
|
||||
}
|
||||
var res = Module._pypy_execute_source(code_ptr);
|
||||
Module._free(code_ptr);
|
||||
// XXX TODO: races/re-entrancy on _lastError?
|
||||
if (pypyjs._lastErrorName) {
|
||||
var err = new pypyjs.Error(
|
||||
pypyjs._lastErrorName,
|
||||
pypyjs._lastErrorMessage,
|
||||
pypyjs._lastErrorTrace
|
||||
);
|
||||
pypyjs._lastErrorName = null;
|
||||
pypyjs._lastErrorMessage = null;
|
||||
pypyjs._lastErrorTrace = null;
|
||||
return Promise.reject(err);
|
||||
}
|
||||
if (res < 0) {
|
||||
return Promise.reject(new pypyjs.Error("Error executing python code"));
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
|
||||
function _escape(value) {
|
||||
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
||||
}
|
||||
|
||||
|
||||
// Method to determine when the interpreter is ready.
|
||||
//
|
||||
// This method returns a promise that will resolve once the interpreter
|
||||
// is ready for use.
|
||||
//
|
||||
pypyjs.prototype.ready = function ready() {
|
||||
return this._ready;
|
||||
}
|
||||
|
||||
|
||||
// Method to execute some python code.
|
||||
//
|
||||
// This passes the given python code to the VM for execution.
|
||||
// It's fairly directly analogous to the "exec" statement in python.
|
||||
// It is not possible to directly access the result of the code, if any.
|
||||
// Rather you should store it into a variable and then use the get() method.
|
||||
//
|
||||
pypyjs.prototype.exec = function exec(code) {
|
||||
return this._ready.then((function() {
|
||||
var p = Promise.resolve();
|
||||
// Find any "import" statements in the code,
|
||||
// and ensure the modules are ready for loading.
|
||||
if (this.autoLoadModules) {
|
||||
p = p.then((function() {
|
||||
return this.findImportedNames(code);
|
||||
}).bind(this))
|
||||
.then((function(imports) {
|
||||
return this.loadModuleData.apply(this, imports);
|
||||
}).bind(this))
|
||||
}
|
||||
// Now we can execute the code in custom top-level scope.
|
||||
code = 'exec \'\'\'' + _escape(code) + '\'\'\' in top_level_scope';
|
||||
p = p.then((function() {
|
||||
return this._execute_source(code);
|
||||
}).bind(this));
|
||||
return p;
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
|
||||
// Method to evaluate an expression.
|
||||
//
|
||||
// This method evaluates an expression and returns its value (assuming the
|
||||
// value can be translated into javascript). It's fairly directly analogous
|
||||
// to the "eval" function in python.
|
||||
//
|
||||
// For backwards-compatibility reasons, it will also evaluate statements.
|
||||
// This behaviour is deprecated and will be removed in a future release.
|
||||
//
|
||||
pypyjs.prototype.eval = function (expr) {
|
||||
return this._ready.then((function() {
|
||||
// First try to execute it as an expression.
|
||||
code = "r = eval('" + _escape(expr) + "', top_level_scope)";
|
||||
return this._execute_source(code);
|
||||
}).bind(this)).then(
|
||||
(function() {
|
||||
// If that succeeded, return the result.
|
||||
return this.get("r", true)
|
||||
}).bind(this),
|
||||
(function(err) {
|
||||
if (err && err.name && err.name !== "SyntaxError") {
|
||||
throw err;
|
||||
}
|
||||
// If that failed, try again via exec().
|
||||
if (typeof console !== "undefined") {
|
||||
console.warn("Calling pypyjs.eval() with statements is deprecated.");
|
||||
console.warn("Use eval() for expressions, exec() for statements.");
|
||||
}
|
||||
return this.exec(expr);
|
||||
}).bind(this)
|
||||
)
|
||||
}
|
||||
|
||||
// Method to evaluate some python code from a file..
|
||||
//
|
||||
// This fetches the named file and passes it to the VM for execution.
|
||||
//
|
||||
pypyjs.prototype.execfile = function execfile(filename) {
|
||||
return this.fetch(filename).then((function(xhr) {
|
||||
var code = xhr.responseText;
|
||||
return this.exec(code);
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
|
||||
// Method to read a python variable.
|
||||
//
|
||||
// This tries to convert the value in the named python variable into an
|
||||
// equivalent javascript value and returns it. It will fail if the variable
|
||||
// does not exist or contains a value that cannot be converted.
|
||||
//
|
||||
pypyjs._resultsID = 0;
|
||||
pypyjs._resultsMap = {};
|
||||
pypyjs.prototype.get = function get(name, _fromGlobals) {
|
||||
var resid = ""+(pypyjs._resultsID++);
|
||||
// We can read from global scope for internal use; don't do this from calling code!
|
||||
if (_fromGlobals) {
|
||||
var namespace = "globals()";
|
||||
} else {
|
||||
var namespace = "top_level_scope";
|
||||
}
|
||||
return this._ready.then((function() {
|
||||
var code = namespace + ".get('" + _escape(name) + "', js.undefined)";
|
||||
code = "js.convert(" + code + ")"
|
||||
code = "js.globals['pypyjs']._resultsMap['" + resid + "'] = " + code;
|
||||
return this._execute_source(code);
|
||||
}).bind(this)).then((function() {
|
||||
var res = pypyjs._resultsMap[resid];
|
||||
delete pypyjs._resultsMap[resid];
|
||||
return res;
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
|
||||
// Method to set a python variable to a javascript value.
|
||||
//
|
||||
// This generates a handle to the given object, and arranges for the named
|
||||
// python variable to reference it via that handle.
|
||||
//
|
||||
pypyjs.prototype.set = function set(name, value) {
|
||||
return this._ready.then((function() {
|
||||
var Module = this._module;
|
||||
var h = Module._emjs_make_handle(value);
|
||||
name = _escape(name);
|
||||
var code = "top_level_scope['" + name + "'] = js.Value(" + h + ")";
|
||||
return this._execute_source(code);
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
|
||||
// Method to run an interactive REPL.
|
||||
//
|
||||
// This method takes takes callback function implementing the user
|
||||
// input prompt, and runs a REPL loop using it. The prompt function
|
||||
// may either return the input as a string, or a promise resolving to
|
||||
// the input as a string. If not specified, we read from stdin (which
|
||||
// works fine in e.g. nodejs, but is almost certainly not what you want
|
||||
// in the browser, because it's blocking).
|
||||
//
|
||||
pypyjs.prototype.repl = function repl(prmpt) {
|
||||
if (!prmpt) {
|
||||
// If there's a custom stdin, or we're not in nodejs, then we should
|
||||
// default to prompting on stdin/stdout. For nodejs, we can build
|
||||
// an async prompt atop process.stdin.
|
||||
var buffer = "";
|
||||
if (this.stdin !== devNull.stdin || typeof process === "undefined") {
|
||||
prmpt = (function(ps1) {
|
||||
var input;
|
||||
this.stdout(ps1);
|
||||
var c = this.stdin();
|
||||
while (c) {
|
||||
var idx = c.indexOf("\n");
|
||||
if (idx >= 0) {
|
||||
var input = buffer + c.substr(0, idx + 1);
|
||||
buffer = c.substr(idx + 1);
|
||||
return input;
|
||||
}
|
||||
buffer += c;
|
||||
c = this.stdin();
|
||||
}
|
||||
input = buffer;
|
||||
buffer = "";
|
||||
return input;
|
||||
}).bind(this);
|
||||
} else {
|
||||
prmpt = (function(ps1) {
|
||||
return new Promise((function(resolve, reject) {
|
||||
this.stdout(ps1);
|
||||
var slurp = function() {
|
||||
process.stdin.once("readable", function() {
|
||||
var chunk = process.stdin.read();
|
||||
if (chunk === null) {
|
||||
slurp();
|
||||
} else {
|
||||
chunk = chunk.toString();
|
||||
var idx = chunk.indexOf("\n");
|
||||
if (idx < 0) {
|
||||
buffer += chunk;
|
||||
slurp();
|
||||
} else {
|
||||
resolve(buffer + chunk.substr(0, idx + 1));
|
||||
buffer = chunk.substr(idx + 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
slurp();
|
||||
}).bind(this));
|
||||
}).bind(this);
|
||||
}
|
||||
}
|
||||
// Set up an InteractiveConsole instance,
|
||||
// then loop forever via recursive promises.
|
||||
return this._ready.then((function() {
|
||||
return this.loadModuleData("code");
|
||||
}).bind(this)).then((function() {
|
||||
return this._execute_source("import code");
|
||||
}).bind(this)).then((function() {
|
||||
return this._execute_source("c = code.InteractiveConsole(top_level_scope)");
|
||||
}).bind(this)).then((function() {
|
||||
return this._repl_loop(prmpt, ">>> ");
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
|
||||
pypyjs.prototype._repl_loop = function _repl_loop(prmpt, ps1) {
|
||||
return Promise.resolve().then((function() {
|
||||
// Prompt for input, which may happen via async promise.
|
||||
return prmpt.call(this, ps1);
|
||||
}).bind(this)).then((function(input) {
|
||||
// Push it into the InteractiveConsole, a line at a time.
|
||||
var p = Promise.resolve();
|
||||
input.split("\n").forEach((function(line) {
|
||||
// Find any "import" statements in the code,
|
||||
// and ensure the modules are ready for loading.
|
||||
if (this.autoLoadModules) {
|
||||
p = p.then((function() {
|
||||
return this.findImportedNames(line);
|
||||
}).bind(this))
|
||||
.then((function(imports) {
|
||||
return this.loadModuleData.apply(this, imports);
|
||||
}).bind(this))
|
||||
}
|
||||
var code = 'r = c.push(\'' + _escape(line) + '\')';
|
||||
p = p.then((function() {
|
||||
return this._execute_source(code);
|
||||
}).bind(this));
|
||||
}).bind(this));
|
||||
return p;
|
||||
}).bind(this)).then((function() {
|
||||
// Check the result from the final push.
|
||||
return this.get("r", true)
|
||||
}).bind(this)).then((function(r) {
|
||||
// If r == 1, we're in a multi-line definition.
|
||||
// Adjust the prompt accordingly.
|
||||
if (r) {
|
||||
return this._repl_loop(prmpt, "... ");
|
||||
} else {
|
||||
return this._repl_loop(prmpt, ">>> ");
|
||||
}
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
|
||||
// Method to look for "import" statements in a code string.
|
||||
// Returns a promise that will resolve to a list of imported module names.
|
||||
//
|
||||
// XXX TODO: this is far from complete and should not be done with a regex.
|
||||
// Perhaps we can call into python's "ast" module for this parsing?
|
||||
//
|
||||
var importStatementRE = /(from\s+([a-zA-Z0-9_\.]+)\s+)?import\s+\(?\s*([a-zA-Z0-9_\.\*]+(\s+as\s+[a-zA-Z0-9_]+)?[ \t]*,?[ \t]*)+[ \t]*\)?/g
|
||||
pypyjs.prototype.findImportedNames = function findImportedNames(code) {
|
||||
var match = null;
|
||||
var imports = [];
|
||||
importStatementRE.lastIndex = 0;
|
||||
while ((match = importStatementRE.exec(code)) !== null) {
|
||||
var relmod = match[2];
|
||||
if (relmod) {
|
||||
relmod = relmod + ".";
|
||||
} else {
|
||||
relmod = "";
|
||||
}
|
||||
var submods = match[0].split("import")[1];
|
||||
while (submods && /[\s(]/.test(submods.charAt(0))) {
|
||||
submods = submods.substr(1);
|
||||
}
|
||||
while (submods && /[\s)]/.test(submods.charAt(submods.length - 1))) {
|
||||
submods = submods.substr(0, submods.length - 1);
|
||||
}
|
||||
submods = submods.split(/\s*,\s*/);
|
||||
for (var i = 0; i < submods.length; i++) {
|
||||
var submod = submods[i];
|
||||
submod = submod.split(/\s*as\s*/)[0];
|
||||
imports.push(relmod + submod);
|
||||
}
|
||||
}
|
||||
return Promise.resolve(imports);
|
||||
}
|
||||
|
||||
|
||||
// Method to load the contents of a python module, along with
|
||||
// any dependencies. This populates the relevant paths within
|
||||
// the VMs simulated filesystem so that is can find and import
|
||||
// the specified module.
|
||||
//
|
||||
pypyjs.prototype.loadModuleData = function loadModuleData(/* names */) {
|
||||
// Each argument is a name that we want to import.
|
||||
// We must find the longest prefix that is an available module
|
||||
// and load it along with all its dependencies.
|
||||
var modules = Array.prototype.slice.call(arguments);
|
||||
return this._ready.then((function() {
|
||||
var toLoad = {};
|
||||
NEXTNAME: for (var i = 0; i < modules.length; i++) {
|
||||
var name = modules[i];
|
||||
// Find the nearest containing module for the given name.
|
||||
// Note that it may not match a module at all, in which case we ignore it.
|
||||
while (true) {
|
||||
if (this._allModules[name]) {
|
||||
break;
|
||||
}
|
||||
name = name.substr(0, name.lastIndexOf("."));
|
||||
if (!name) continue NEXTNAME;
|
||||
}
|
||||
this._findModuleDeps(name, toLoad);
|
||||
}
|
||||
// Now ensure that each module gets loaded.
|
||||
// XXX TODO: we could load these concurrently.
|
||||
var p = Promise.resolve();
|
||||
for (var name in toLoad) {
|
||||
p = p.then(this._makeLoadModuleData(name));
|
||||
}
|
||||
return p;
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
|
||||
pypyjs.prototype._findModuleDeps = function _findModuleDeps(name, seen) {
|
||||
if (!seen) seen = {};
|
||||
var deps = [];
|
||||
// If we don't know about this module, ignore it.
|
||||
if (!this._allModules[name]) {
|
||||
return seen;
|
||||
}
|
||||
// Depend on any explicitly-named imports.
|
||||
var imports = this._allModules[name].imports;
|
||||
if (imports) {
|
||||
for (var i = 0; i < imports.length; i++) {
|
||||
deps.push(imports[i]);
|
||||
}
|
||||
}
|
||||
// Depend on the __init__.py for packages.
|
||||
if (this._allModules[name].dir) {
|
||||
deps.push(name + ".__init__");
|
||||
}
|
||||
// Include the parent package, if any.
|
||||
var idx = name.lastIndexOf(".");
|
||||
if (idx !== -1) {
|
||||
deps.push(name.substr(0, idx));
|
||||
}
|
||||
// Recurse for any previously-unseen dependencies.
|
||||
seen[name] = true;
|
||||
for (var i = 0; i < deps.length; i++) {
|
||||
if (!seen[deps[i]]) {
|
||||
this._findModuleDeps(deps[i], seen);
|
||||
}
|
||||
}
|
||||
return seen;
|
||||
}
|
||||
|
||||
|
||||
pypyjs.prototype._makeLoadModuleData = function _makeLoadModuleData(name) {
|
||||
return (function() {
|
||||
// If we've already loaded this module, we're done.
|
||||
if (this._loadedModules[name]) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
// If we're already in the process of loading it, use the existing promise.
|
||||
if (this._pendingModules[name]) {
|
||||
return this._pendingModules[name];
|
||||
}
|
||||
// If it's a package directory, there's not actually anything to do.
|
||||
if (this._allModules[name].dir) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
// We need to fetch the module file and write it out.
|
||||
var modfile = this._allModules[name].file;
|
||||
var p = this.fetch("modules/" + modfile)
|
||||
.then((function(xhr) {
|
||||
var contents = xhr.responseText;
|
||||
this._writeModuleFile(name, contents)
|
||||
delete this._pendingModules[name];
|
||||
}).bind(this))
|
||||
this._pendingModules[name] = p;
|
||||
return p;
|
||||
}).bind(this);
|
||||
}
|
||||
|
||||
|
||||
pypyjs.prototype._writeModuleFile = function _writeModuleFile(name, data) {
|
||||
var Module = this._module;
|
||||
var file = this._allModules[name].file;
|
||||
// Create the containing directory first.
|
||||
var dir = file.split("/").slice(0, -1).join("/")
|
||||
try {
|
||||
Module.FS_createPath("/lib/pypyjs/lib_pypy", dir, true, false);
|
||||
} catch (e) { }
|
||||
// Now we can safely create the file.
|
||||
var fullpath = "/lib/pypyjs/lib_pypy/" + file;
|
||||
Module.FS_createDataFile(fullpath, "", data, true, false, true);
|
||||
this._loadedModules[name] = true;
|
||||
}
|
||||
|
||||
|
||||
// An error class for reporting python exceptions back to calling code.
|
||||
// XXX TODO: this could be a lot more user-friendly than a opaque error...
|
||||
|
||||
pypyjs.Error = function pypyjsError(name, message, trace) {
|
||||
if (name && typeof message === "undefined") {
|
||||
message = name;
|
||||
name = "";
|
||||
}
|
||||
this.name = name || "pypyjs.Error";
|
||||
this.message = message || "pypyjs Unknown Error";
|
||||
this.trace = trace || "";
|
||||
}
|
||||
pypyjs.Error.prototype = new Error();
|
||||
pypyjs.Error.prototype.constructor = pypyjs.Error;
|
||||
|
||||
|
||||
|
||||
// XXX TODO: expose the filesystem for manipulation by calling code.
|
||||
|
||||
|
||||
// Add convenience methods directly on the 'pypyjs' function, that
|
||||
// will invoke corresponding methods on a default VM instance.
|
||||
// This makes it look like 'pypyjs' is a singleton VM instance.
|
||||
|
||||
pypyjs._defaultVM = null;
|
||||
pypyjs.stdin = stdio.stdin
|
||||
pypyjs.stdout = stdio.stdout
|
||||
pypyjs.stderr = stdio.stderr
|
||||
|
||||
var PUBLIC_NAMES = ['ready', 'exec', 'eval', 'execfile', 'get', 'set',
|
||||
'repl', 'loadModuleData'];
|
||||
|
||||
PUBLIC_NAMES.forEach(function(name) {
|
||||
pypyjs[name] = function() {
|
||||
if (!pypyjs._defaultVM) {
|
||||
pypyjs._defaultVM = new pypyjs({
|
||||
stdin: function(){ return pypyjs.stdin.apply(this, arguments); },
|
||||
stdout: function(){ return pypyjs.stdout.apply(this, arguments); },
|
||||
stderr: function(){ return pypyjs.stderr.apply(this, arguments); },
|
||||
});
|
||||
}
|
||||
return pypyjs._defaultVM[name].apply(pypyjs._defaultVM, arguments)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// For nodejs, run a repl when invoked directly from the command-line.
|
||||
|
||||
return pypyjs;
|
||||
|
||||
})();
|
150
docs/playground/page.js
Normal file
150
docs/playground/page.js
Normal file
|
@ -0,0 +1,150 @@
|
|||
import React from 'react';
|
||||
import GraphenePlayground from './GraphenePlayground';
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
const DEFAULT_CACHE_KEY = 'default';
|
||||
|
||||
function filterObject(object, callback, context) {
|
||||
if (!object) {
|
||||
return null;
|
||||
}
|
||||
var result = {};
|
||||
for (var name in object) {
|
||||
if (hasOwnProperty.call(object, name) &&
|
||||
callback.call(context, object[name], name, object)) {
|
||||
result[name] = object[name];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class Playground extends React.Component {
|
||||
componentWillMount() {
|
||||
var sourceWasInjected = false;
|
||||
var queryParams = this.context.router.getCurrentQuery();
|
||||
|
||||
var {
|
||||
cacheKey,
|
||||
noCache,
|
||||
} = queryParams;
|
||||
noCache = (noCache !== undefined) && (noCache !== 'false');
|
||||
if (noCache) {
|
||||
cacheKey = undefined;
|
||||
} else if (!cacheKey) {
|
||||
cacheKey = DEFAULT_CACHE_KEY;
|
||||
}
|
||||
this.schemaCacheKey = `rp-${cacheKey}-schema`;
|
||||
this.queryCacheKey = `rp-${cacheKey}-query`;
|
||||
this.cacheKey = cacheKey;
|
||||
|
||||
var initialSchema;
|
||||
var initialQuery;
|
||||
var storedSchema = localStorage.getItem(this.schemaCacheKey);
|
||||
var storedQuery = localStorage.getItem(this.queryCacheKey);
|
||||
if (noCache) {
|
||||
// Use case #1
|
||||
// We use the noCache param to force a playground to have certain contents.
|
||||
// eg. static example apps
|
||||
initialSchema = queryParams.schema || '';
|
||||
initialQuery = queryParams.query || '';
|
||||
sourceWasInjected = true;
|
||||
queryParams = {};
|
||||
} else if (cacheKey === DEFAULT_CACHE_KEY) {
|
||||
// Use case #2
|
||||
// The user loaded the playground without a custom cache key.
|
||||
// Allow code injection via the URL
|
||||
// OR load code from localStorage
|
||||
// OR prime the playground with some default 'hello world' code
|
||||
if (queryParams.schema != null) {
|
||||
initialSchema = queryParams.schema;
|
||||
sourceWasInjected = queryParams.schema !== storedSchema;
|
||||
} else if (storedSchema != null) {
|
||||
initialSchema = storedSchema;
|
||||
} else {
|
||||
initialSchema = require('!raw!./examples/hello.schema.py');
|
||||
}
|
||||
if (queryParams.query != null) {
|
||||
initialQuery = queryParams.query;
|
||||
sourceWasInjected = queryParams.query !== storedQuery;
|
||||
} else if (storedQuery != null) {
|
||||
initialQuery = storedQuery;
|
||||
} else {
|
||||
initialQuery = require('!raw!./examples/hello.graphql');
|
||||
}
|
||||
queryParams = filterObject({
|
||||
schema: queryParams.schema,
|
||||
query: queryParams.query,
|
||||
}, v => v !== undefined);
|
||||
} else if (cacheKey) {
|
||||
// Use case #3
|
||||
// Custom cache keys are useful in cases where you want to embed a playground
|
||||
// that features both custom boilerplate code AND saves the developer's
|
||||
// progress, without overwriting the default code cache. eg. a tutorial.
|
||||
if (storedSchema != null) {
|
||||
initialSchema = storedSchema;
|
||||
} else {
|
||||
initialSchema = queryParams[`schema_${cacheKey}`];
|
||||
if (initialSchema != null) {
|
||||
sourceWasInjected = true;
|
||||
}
|
||||
}
|
||||
if (storedQuery != null) {
|
||||
initialQuery = storedQuery;
|
||||
} else {
|
||||
initialQuery = queryParams[`query_${cacheKey}`];
|
||||
if (initialQuery != null) {
|
||||
sourceWasInjected = true;
|
||||
}
|
||||
}
|
||||
queryParams = {};
|
||||
}
|
||||
this.changeParams(queryParams);
|
||||
this.state = {initialSchema, initialQuery, sourceWasInjected};
|
||||
this.queryParams = queryParams;
|
||||
}
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
changeParams(queryParams) {
|
||||
var router = this.context.router;
|
||||
var routeName = router.getCurrentPathname();
|
||||
var params = router.getCurrentParams();
|
||||
queryParams = _.mapValues(queryParams, encodeURIComponent);
|
||||
router.replaceWith(routeName, params, queryParams);
|
||||
}
|
||||
render() {
|
||||
return (<GraphenePlayground
|
||||
initialSchema={this.state.initialSchema}
|
||||
initialQuery={this.state.initialQuery}
|
||||
onEditSchema={(source) => {
|
||||
localStorage.setItem(this.schemaCacheKey, source);
|
||||
if (this.cacheKey === DEFAULT_CACHE_KEY) {
|
||||
this.queryParams.schema = source;
|
||||
if (!this.queryParams.query) {
|
||||
this.queryParams.query = this.state.initialQuery;
|
||||
}
|
||||
this.changeParams(this.queryParams);
|
||||
}
|
||||
}}
|
||||
onEditQuery={(source) => {
|
||||
localStorage.setItem(this.queryCacheKey, source);
|
||||
if (this.cacheKey === DEFAULT_CACHE_KEY) {
|
||||
this.queryParams.query = source;
|
||||
if (!this.queryParams.schema) {
|
||||
this.queryParams.schema = this.state.initialSchema;
|
||||
}
|
||||
this.changeParams(this.queryParams);
|
||||
}
|
||||
}}
|
||||
/>);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Playground.contextTypes = {
|
||||
router: React.PropTypes.func
|
||||
};
|
||||
|
||||
module.exports = Playground;
|
15
docs/playground/schema.js
Normal file
15
docs/playground/schema.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import {
|
||||
GraphQLObjectType,
|
||||
GraphQLString,
|
||||
GraphQLSchema,
|
||||
} from 'graphql';
|
||||
|
||||
|
||||
export default new GraphQLSchema({
|
||||
query: new GraphQLObjectType({
|
||||
name: 'Query',
|
||||
fields: () => ({
|
||||
__emptyField: {type: GraphQLString},
|
||||
}),
|
||||
}),
|
||||
});
|
21
docs/playground/wrapper.js
Normal file
21
docs/playground/wrapper.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
|
||||
class PlaygroundWrapper extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { currentComponent: null };
|
||||
}
|
||||
componentDidMount() {
|
||||
require(["playground-page"], (Playground) =>{
|
||||
this.setState({
|
||||
currentComponent: Playground
|
||||
});
|
||||
});
|
||||
}
|
||||
render() {
|
||||
var Current = this.state.currentComponent;
|
||||
return Current?<Current />:null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PlaygroundWrapper;
|
1
docs/static/CNAME
vendored
Normal file
1
docs/static/CNAME
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
graphene-python.org
|
BIN
docs/static/favicon.png
vendored
Normal file
BIN
docs/static/favicon.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
796
docs/vendor/glfx.optim.js
vendored
Normal file
796
docs/vendor/glfx.optim.js
vendored
Normal file
|
@ -0,0 +1,796 @@
|
|||
/*
|
||||
* glfx.js
|
||||
* http://evanw.github.com/glfx.js/
|
||||
*
|
||||
* Copyright 2011 Evan Wallace
|
||||
* Released under the MIT license
|
||||
*/
|
||||
module.exports = function() {
|
||||
function q(a, d, c) {
|
||||
return Math.max(a, Math.min(d, c))
|
||||
}
|
||||
|
||||
function w(b) {
|
||||
return {
|
||||
_: b,
|
||||
loadContentsOf: function(b) {
|
||||
a = this._.gl;
|
||||
this._.loadContentsOf(b)
|
||||
},
|
||||
destroy: function() {
|
||||
a = this._.gl;
|
||||
this._.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function A(a) {
|
||||
return w(r.fromElement(a))
|
||||
}
|
||||
|
||||
function B(b, d) {
|
||||
var c = a.UNSIGNED_BYTE;
|
||||
if (a.getExtension("OES_texture_float") && a.getExtension("OES_texture_float_linear")) {
|
||||
var e = new r(100, 100, a.RGBA, a.FLOAT);
|
||||
try {
|
||||
e.drawTo(function() {
|
||||
c = a.FLOAT
|
||||
})
|
||||
} catch (g) {}
|
||||
e.destroy()
|
||||
}
|
||||
this._.texture && this._.texture.destroy();
|
||||
this._.spareTexture && this._.spareTexture.destroy();
|
||||
this.width = b;
|
||||
this.height = d;
|
||||
this._.texture = new r(b, d, a.RGBA, c);
|
||||
this._.spareTexture = new r(b, d, a.RGBA, c);
|
||||
this._.extraTexture = this._.extraTexture || new r(0, 0, a.RGBA, c);
|
||||
this._.flippedShader = this._.flippedShader || new h(null, "uniform sampler2D texture;varying vec2 texCoord;void main(){gl_FragColor=texture2D(texture,vec2(texCoord.x,1.0-texCoord.y));}");
|
||||
this._.isInitialized = !0
|
||||
}
|
||||
|
||||
function C(a, d, c) {
|
||||
this._.isInitialized &&
|
||||
a._.width == this.width && a._.height == this.height || B.call(this, d ? d : a._.width, c ? c : a._.height);
|
||||
a._.use();
|
||||
this._.texture.drawTo(function() {
|
||||
h.getDefaultShader().drawRect()
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function D() {
|
||||
this._.texture.use();
|
||||
this._.flippedShader.drawRect();
|
||||
return this
|
||||
}
|
||||
|
||||
function f(a, d, c, e) {
|
||||
(c || this._.texture).use();
|
||||
this._.spareTexture.drawTo(function() {
|
||||
a.uniforms(d).drawRect()
|
||||
});
|
||||
this._.spareTexture.swapWith(e || this._.texture)
|
||||
}
|
||||
|
||||
function E(a) {
|
||||
a.parentNode.insertBefore(this, a);
|
||||
a.parentNode.removeChild(a);
|
||||
return this
|
||||
}
|
||||
|
||||
function F() {
|
||||
var b = new r(this._.texture.width, this._.texture.height, a.RGBA, a.UNSIGNED_BYTE);
|
||||
this._.texture.use();
|
||||
b.drawTo(function() {
|
||||
h.getDefaultShader().drawRect()
|
||||
});
|
||||
return w(b)
|
||||
}
|
||||
|
||||
function G() {
|
||||
var b = this._.texture.width,
|
||||
d = this._.texture.height,
|
||||
c = new Uint8Array(4 * b * d);
|
||||
this._.texture.drawTo(function() {
|
||||
a.readPixels(0, 0, b, d, a.RGBA, a.UNSIGNED_BYTE, c)
|
||||
});
|
||||
return c
|
||||
}
|
||||
|
||||
function k(b) {
|
||||
return function() {
|
||||
a = this._.gl;
|
||||
return b.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
function x(a, d, c, e, g, l, n, p) {
|
||||
var m = c - g,
|
||||
h = e - l,
|
||||
f = n - g,
|
||||
k = p - l;
|
||||
g = a - c + g - n;
|
||||
l =
|
||||
d - e + l - p;
|
||||
var q = m * k - f * h,
|
||||
f = (g * k - f * l) / q,
|
||||
m = (m * l - g * h) / q;
|
||||
return [c - a + f * c, e - d + f * e, f, n - a + m * n, p - d + m * p, m, a, d, 1]
|
||||
}
|
||||
|
||||
function y(a) {
|
||||
var d = a[0],
|
||||
c = a[1],
|
||||
e = a[2],
|
||||
g = a[3],
|
||||
l = a[4],
|
||||
n = a[5],
|
||||
p = a[6],
|
||||
m = a[7];
|
||||
a = a[8];
|
||||
var f = d * l * a - d * n * m - c * g * a + c * n * p + e * g * m - e * l * p;
|
||||
return [(l * a - n * m) / f, (e * m - c * a) / f, (c * n - e * l) / f, (n * p - g * a) / f, (d * a - e * p) / f, (e * g - d * n) / f, (g * m - l * p) / f, (c * p - d * m) / f, (d * l - c * g) / f]
|
||||
}
|
||||
|
||||
function z(a) {
|
||||
var d = a.length;
|
||||
this.xa = [];
|
||||
this.ya = [];
|
||||
this.u = [];
|
||||
this.y2 = [];
|
||||
a.sort(function(a, b) {
|
||||
return a[0] - b[0]
|
||||
});
|
||||
for (var c = 0; c < d; c++) this.xa.push(a[c][0]), this.ya.push(a[c][1]);
|
||||
this.u[0] = 0;
|
||||
this.y2[0] = 0;
|
||||
for (c = 1; c < d - 1; ++c) {
|
||||
a = this.xa[c + 1] - this.xa[c - 1];
|
||||
var e = (this.xa[c] - this.xa[c - 1]) / a,
|
||||
g = e * this.y2[c - 1] + 2;
|
||||
this.y2[c] = (e - 1) / g;
|
||||
this.u[c] = (6 * ((this.ya[c + 1] - this.ya[c]) / (this.xa[c + 1] - this.xa[c]) - (this.ya[c] - this.ya[c - 1]) / (this.xa[c] - this.xa[c - 1])) / a - e * this.u[c - 1]) / g
|
||||
}
|
||||
this.y2[d - 1] = 0;
|
||||
for (c = d - 2; 0 <= c; --c) this.y2[c] = this.y2[c] * this.y2[c + 1] + this.u[c]
|
||||
}
|
||||
|
||||
function u(a, d) {
|
||||
return new h(null, a + "uniform sampler2D texture;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 coord=texCoord*texSize;" +
|
||||
d + "gl_FragColor=texture2D(texture,coord/texSize);vec2 clampedCoord=clamp(coord,vec2(0.0),texSize);if(coord!=clampedCoord){gl_FragColor.a*=max(0.0,1.0-length(coord-clampedCoord));}}")
|
||||
}
|
||||
|
||||
function H(b, d) {
|
||||
a.brightnessContrast = a.brightnessContrast || new h(null, "uniform sampler2D texture;uniform float brightness;uniform float contrast;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.rgb+=brightness;if(contrast>0.0){color.rgb=(color.rgb-0.5)/(1.0-contrast)+0.5;}else{color.rgb=(color.rgb-0.5)*(1.0+contrast)+0.5;}gl_FragColor=color;}");
|
||||
f.call(this, a.brightnessContrast, {
|
||||
brightness: q(-1, b, 1),
|
||||
contrast: q(-1, d, 1)
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function t(a) {
|
||||
a = new z(a);
|
||||
for (var d = [], c = 0; 256 > c; c++) d.push(q(0, Math.floor(256 * a.interpolate(c / 255)), 255));
|
||||
return d
|
||||
}
|
||||
|
||||
function I(b, d, c) {
|
||||
b = t(b);
|
||||
1 == arguments.length ? d = c = b : (d = t(d), c = t(c));
|
||||
for (var e = [], g = 0; 256 > g; g++) e.splice(e.length, 0, b[g], d[g], c[g], 255);
|
||||
this._.extraTexture.initFromBytes(256, 1, e);
|
||||
this._.extraTexture.use(1);
|
||||
a.curves = a.curves || new h(null, "uniform sampler2D texture;uniform sampler2D map;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.r=texture2D(map,vec2(color.r)).r;color.g=texture2D(map,vec2(color.g)).g;color.b=texture2D(map,vec2(color.b)).b;gl_FragColor=color;}");
|
||||
a.curves.textures({
|
||||
map: 1
|
||||
});
|
||||
f.call(this, a.curves, {});
|
||||
return this
|
||||
}
|
||||
|
||||
function J(b) {
|
||||
a.denoise = a.denoise || new h(null, "uniform sampler2D texture;uniform float exponent;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;void main(){vec4 center=texture2D(texture,texCoord);vec4 color=vec4(0.0);float total=0.0;for(float x=-4.0;x<=4.0;x+=1.0){for(float y=-4.0;y<=4.0;y+=1.0){vec4 sample=texture2D(texture,texCoord+vec2(x,y)/texSize);float weight=1.0-abs(dot(sample.rgb-center.rgb,vec3(0.25)));weight=pow(weight,exponent);color+=sample*weight;total+=weight;}}gl_FragColor=color/total;}");
|
||||
for (var d = 0; 2 > d; d++) f.call(this, a.denoise, {
|
||||
exponent: Math.max(0, b),
|
||||
texSize: [this.width, this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function K(b, d) {
|
||||
a.hueSaturation = a.hueSaturation || new h(null, "uniform sampler2D texture;uniform float hue;uniform float saturation;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float angle=hue*3.14159265;float s=sin(angle),c=cos(angle);vec3 weights=(vec3(2.0*c,-sqrt(3.0)*s-c,sqrt(3.0)*s-c)+1.0)/3.0;float len=length(color.rgb);color.rgb=vec3(dot(color.rgb,weights.xyz),dot(color.rgb,weights.zxy),dot(color.rgb,weights.yzx));float average=(color.r+color.g+color.b)/3.0;if(saturation>0.0){color.rgb+=(average-color.rgb)*(1.0-1.0/(1.001-saturation));}else{color.rgb+=(average-color.rgb)*(-saturation);}gl_FragColor=color;}");
|
||||
f.call(this, a.hueSaturation, {
|
||||
hue: q(-1, b, 1),
|
||||
saturation: q(-1, d, 1)
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function L(b) {
|
||||
a.noise = a.noise || new h(null, "uniform sampler2D texture;uniform float amount;varying vec2 texCoord;float rand(vec2 co){return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453);}void main(){vec4 color=texture2D(texture,texCoord);float diff=(rand(texCoord)-0.5)*amount;color.r+=diff;color.g+=diff;color.b+=diff;gl_FragColor=color;}");
|
||||
f.call(this, a.noise, {
|
||||
amount: q(0, b, 1)
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function M(b) {
|
||||
a.sepia = a.sepia || new h(null, "uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float r=color.r;float g=color.g;float b=color.b;color.r=min(1.0,(r*(1.0-(0.607*amount)))+(g*(0.769*amount))+(b*(0.189*amount)));color.g=min(1.0,(r*0.349*amount)+(g*(1.0-(0.314*amount)))+(b*0.168*amount));color.b=min(1.0,(r*0.272*amount)+(g*0.534*amount)+(b*(1.0-(0.869*amount))));gl_FragColor=color;}");
|
||||
f.call(this, a.sepia, {
|
||||
amount: q(0, b, 1)
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function N(b, d) {
|
||||
a.unsharpMask = a.unsharpMask || new h(null, "uniform sampler2D blurredTexture;uniform sampler2D originalTexture;uniform float strength;uniform float threshold;varying vec2 texCoord;void main(){vec4 blurred=texture2D(blurredTexture,texCoord);vec4 original=texture2D(originalTexture,texCoord);gl_FragColor=mix(blurred,original,1.0+strength);}");
|
||||
this._.extraTexture.ensureFormat(this._.texture);
|
||||
this._.texture.use();
|
||||
this._.extraTexture.drawTo(function() {
|
||||
h.getDefaultShader().drawRect()
|
||||
});
|
||||
this._.extraTexture.use(1);
|
||||
this.triangleBlur(b);
|
||||
a.unsharpMask.textures({
|
||||
originalTexture: 1
|
||||
});
|
||||
f.call(this, a.unsharpMask, {
|
||||
strength: d
|
||||
});
|
||||
this._.extraTexture.unuse(1);
|
||||
return this
|
||||
}
|
||||
|
||||
function O(b) {
|
||||
a.vibrance = a.vibrance || new h(null, "uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;float mx=max(color.r,max(color.g,color.b));float amt=(mx-average)*(-amount*3.0);color.rgb=mix(color.rgb,vec3(mx),amt);gl_FragColor=color;}");
|
||||
f.call(this, a.vibrance, {
|
||||
amount: q(-1, b, 1)
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function P(b, d) {
|
||||
a.vignette = a.vignette || new h(null, "uniform sampler2D texture;uniform float size;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float dist=distance(texCoord,vec2(0.5,0.5));color.rgb*=smoothstep(0.8,size*0.799,dist*(amount+size));gl_FragColor=color;}");
|
||||
f.call(this, a.vignette, {
|
||||
size: q(0, b, 1),
|
||||
amount: q(0, d, 1)
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function Q(b, d, c) {
|
||||
a.lensBlurPrePass = a.lensBlurPrePass || new h(null, "uniform sampler2D texture;uniform float power;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color=pow(color,vec4(power));gl_FragColor=vec4(color);}");
|
||||
var e = "uniform sampler2D texture0;uniform sampler2D texture1;uniform vec2 delta0;uniform vec2 delta1;uniform float power;varying vec2 texCoord;" +
|
||||
s + "vec4 sample(vec2 delta){float offset=random(vec3(delta,151.7182),0.0);vec4 color=vec4(0.0);float total=0.0;for(float t=0.0;t<=30.0;t++){float percent=(t+offset)/30.0;color+=texture2D(texture0,texCoord+delta*percent);total+=1.0;}return color/total;}";
|
||||
a.lensBlur0 = a.lensBlur0 || new h(null, e + "void main(){gl_FragColor=sample(delta0);}");
|
||||
a.lensBlur1 = a.lensBlur1 || new h(null, e + "void main(){gl_FragColor=(sample(delta0)+sample(delta1))*0.5;}");
|
||||
a.lensBlur2 = a.lensBlur2 || (new h(null, e + "void main(){vec4 color=(sample(delta0)+2.0*texture2D(texture1,texCoord))/3.0;gl_FragColor=pow(color,vec4(power));}")).textures({
|
||||
texture1: 1
|
||||
});
|
||||
for (var e = [], g = 0; 3 > g; g++) {
|
||||
var l = c + 2 * g * Math.PI / 3;
|
||||
e.push([b * Math.sin(l) / this.width, b * Math.cos(l) / this.height])
|
||||
}
|
||||
b = Math.pow(10, q(-1, d, 1));
|
||||
f.call(this, a.lensBlurPrePass, {
|
||||
power: b
|
||||
});
|
||||
this._.extraTexture.ensureFormat(this._.texture);
|
||||
f.call(this, a.lensBlur0, {
|
||||
delta0: e[0]
|
||||
}, this._.texture, this._.extraTexture);
|
||||
f.call(this, a.lensBlur1, {
|
||||
delta0: e[1],
|
||||
delta1: e[2]
|
||||
}, this._.extraTexture, this._.extraTexture);
|
||||
f.call(this, a.lensBlur0, {
|
||||
delta0: e[1]
|
||||
});
|
||||
this._.extraTexture.use(1);
|
||||
f.call(this, a.lensBlur2, {
|
||||
power: 1 / b,
|
||||
delta0: e[2]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function R(b, d, c, e, g, l) {
|
||||
a.tiltShift = a.tiltShift || new h(null, "uniform sampler2D texture;uniform float blurRadius;uniform float gradientRadius;uniform vec2 start;uniform vec2 end;uniform vec2 delta;uniform vec2 texSize;varying vec2 texCoord;" + s + "void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);vec2 normal=normalize(vec2(start.y-end.y,end.x-start.x));float radius=smoothstep(0.0,1.0,abs(dot(texCoord*texSize-start,normal))/gradientRadius)*blurRadius;for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta/texSize*percent*radius);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
|
||||
var n = c - b,
|
||||
p = e - d,
|
||||
m = Math.sqrt(n * n + p * p);
|
||||
f.call(this, a.tiltShift, {
|
||||
blurRadius: g,
|
||||
gradientRadius: l,
|
||||
start: [b, d],
|
||||
end: [c, e],
|
||||
delta: [n / m, p / m],
|
||||
texSize: [this.width, this.height]
|
||||
});
|
||||
f.call(this, a.tiltShift, {
|
||||
blurRadius: g,
|
||||
gradientRadius: l,
|
||||
start: [b, d],
|
||||
end: [c, e],
|
||||
delta: [-p / m, n / m],
|
||||
texSize: [this.width, this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function S(b) {
|
||||
a.triangleBlur = a.triangleBlur || new h(null, "uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;" + s + "void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta*percent);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
|
||||
f.call(this, a.triangleBlur, {
|
||||
delta: [b / this.width, 0]
|
||||
});
|
||||
f.call(this, a.triangleBlur, {
|
||||
delta: [0, b / this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function T(b, d, c) {
|
||||
a.zoomBlur = a.zoomBlur || new h(null, "uniform sampler2D texture;uniform vec2 center;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;" + s + "void main(){vec4 color=vec4(0.0);float total=0.0;vec2 toCenter=center-texCoord*texSize;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=0.0;t<=40.0;t++){float percent=(t+offset)/40.0;float weight=4.0*(percent-percent*percent);vec4 sample=texture2D(texture,texCoord+toCenter*percent*strength/texSize);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
|
||||
f.call(this, a.zoomBlur, {
|
||||
center: [b, d],
|
||||
strength: c,
|
||||
texSize: [this.width, this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function U(b, d, c, e) {
|
||||
a.colorHalftone = a.colorHalftone || new h(null, "uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(float angle){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);vec3 cmy=1.0-color.rgb;float k=min(cmy.x,min(cmy.y,cmy.z));cmy=(cmy-k)/(1.0-k);cmy=clamp(cmy*10.0-3.0+vec3(pattern(angle+0.26179),pattern(angle+1.30899),pattern(angle)),0.0,1.0);k=clamp(k*10.0-5.0+pattern(angle+0.78539),0.0,1.0);gl_FragColor=vec4(1.0-cmy-k,color.a);}");
|
||||
f.call(this, a.colorHalftone, {
|
||||
center: [b, d],
|
||||
angle: c,
|
||||
scale: Math.PI / e,
|
||||
texSize: [this.width, this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function V(b, d, c, e) {
|
||||
a.dotScreen = a.dotScreen || new h(null, "uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;gl_FragColor=vec4(vec3(average*10.0-5.0+pattern()),color.a);}");
|
||||
f.call(this, a.dotScreen, {
|
||||
center: [b, d],
|
||||
angle: c,
|
||||
scale: Math.PI / e,
|
||||
texSize: [this.width, this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function W(b) {
|
||||
a.edgeWork1 = a.edgeWork1 || new h(null, "uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;" + s + "void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec3 sample=texture2D(texture,texCoord+delta*percent).rgb;float average=(sample.r+sample.g+sample.b)/3.0;color.x+=average*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=average*weight;total.y+=weight;}}gl_FragColor=vec4(color/total,0.0,1.0);}");
|
||||
a.edgeWork2 = a.edgeWork2 || new h(null, "uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;" + s + "void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec2 sample=texture2D(texture,texCoord+delta*percent).xy;color.x+=sample.x*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=sample.y*weight;total.y+=weight;}}float c=clamp(10000.0*(color.y/total.y-color.x/total.x)+0.5,0.0,1.0);gl_FragColor=vec4(c,c,c,1.0);}");
|
||||
f.call(this, a.edgeWork1, {
|
||||
delta: [b / this.width, 0]
|
||||
});
|
||||
f.call(this, a.edgeWork2, {
|
||||
delta: [0, b / this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function X(b, d, c) {
|
||||
a.hexagonalPixelate = a.hexagonalPixelate || new h(null, "uniform sampler2D texture;uniform vec2 center;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 tex=(texCoord*texSize-center)/scale;tex.y/=0.866025404;tex.x-=tex.y*0.5;vec2 a;if(tex.x+tex.y-floor(tex.x)-floor(tex.y)<1.0)a=vec2(floor(tex.x),floor(tex.y));else a=vec2(ceil(tex.x),ceil(tex.y));vec2 b=vec2(ceil(tex.x),floor(tex.y));vec2 c=vec2(floor(tex.x),ceil(tex.y));vec3 TEX=vec3(tex.x,tex.y,1.0-tex.x-tex.y);vec3 A=vec3(a.x,a.y,1.0-a.x-a.y);vec3 B=vec3(b.x,b.y,1.0-b.x-b.y);vec3 C=vec3(c.x,c.y,1.0-c.x-c.y);float alen=length(TEX-A);float blen=length(TEX-B);float clen=length(TEX-C);vec2 choice;if(alen<blen){if(alen<clen)choice=a;else choice=c;}else{if(blen<clen)choice=b;else choice=c;}choice.x+=choice.y*0.5;choice.y*=0.866025404;choice*=scale/texSize;gl_FragColor=texture2D(texture,choice+center/texSize);}");
|
||||
f.call(this, a.hexagonalPixelate, {
|
||||
center: [b, d],
|
||||
scale: c,
|
||||
texSize: [this.width, this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function Y(b) {
|
||||
a.ink = a.ink || new h(null, "uniform sampler2D texture;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 dx=vec2(1.0/texSize.x,0.0);vec2 dy=vec2(0.0,1.0/texSize.y);vec4 color=texture2D(texture,texCoord);float bigTotal=0.0;float smallTotal=0.0;vec3 bigAverage=vec3(0.0);vec3 smallAverage=vec3(0.0);for(float x=-2.0;x<=2.0;x+=1.0){for(float y=-2.0;y<=2.0;y+=1.0){vec3 sample=texture2D(texture,texCoord+dx*x+dy*y).rgb;bigAverage+=sample;bigTotal+=1.0;if(abs(x)+abs(y)<2.0){smallAverage+=sample;smallTotal+=1.0;}}}vec3 edge=max(vec3(0.0),bigAverage/bigTotal-smallAverage/smallTotal);gl_FragColor=vec4(color.rgb-dot(edge,edge)*strength*100000.0,color.a);}");
|
||||
f.call(this, a.ink, {
|
||||
strength: b * b * b * b * b,
|
||||
texSize: [this.width, this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function Z(b, d, c, e) {
|
||||
a.bulgePinch = a.bulgePinch || u("uniform float radius;uniform float strength;uniform vec2 center;", "coord-=center;float distance=length(coord);if(distance<radius){float percent=distance/radius;if(strength>0.0){coord*=mix(1.0,smoothstep(0.0,radius/distance,percent),strength*0.75);}else{coord*=mix(1.0,pow(percent,1.0+strength*0.75)*radius/distance,1.0-percent);}}coord+=center;");
|
||||
f.call(this, a.bulgePinch, {
|
||||
radius: c,
|
||||
strength: q(-1, e, 1),
|
||||
center: [b, d],
|
||||
texSize: [this.width, this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function $(b, d, c) {
|
||||
a.matrixWarp = a.matrixWarp || u("uniform mat3 matrix;uniform bool useTextureSpace;", "if(useTextureSpace)coord=coord/texSize*2.0-1.0;vec3 warp=matrix*vec3(coord,1.0);coord=warp.xy/warp.z;if(useTextureSpace)coord=(coord*0.5+0.5)*texSize;");
|
||||
b = Array.prototype.concat.apply([], b);
|
||||
if (4 == b.length) b = [b[0], b[1], 0, b[2], b[3], 0, 0, 0, 1];
|
||||
else if (9 != b.length) throw "can only warp with 2x2 or 3x3 matrix";
|
||||
f.call(this, a.matrixWarp, {
|
||||
matrix: d ? y(b) : b,
|
||||
texSize: [this.width, this.height],
|
||||
useTextureSpace: c | 0
|
||||
});
|
||||
return this
|
||||
}
|
||||
|
||||
function aa(a, d) {
|
||||
var c = x.apply(null, d),
|
||||
e = x.apply(null, a),
|
||||
c = y(c);
|
||||
return this.matrixWarp([c[0] * e[0] + c[1] * e[3] + c[2] * e[6], c[0] * e[1] + c[1] * e[4] + c[2] * e[7], c[0] * e[2] + c[1] * e[5] + c[2] * e[8], c[3] * e[0] + c[4] * e[3] + c[5] * e[6], c[3] * e[1] + c[4] * e[4] + c[5] * e[7], c[3] * e[2] + c[4] * e[5] + c[5] * e[8], c[6] * e[0] + c[7] * e[3] + c[8] * e[6],
|
||||
c[6] * e[1] + c[7] * e[4] + c[8] * e[7], c[6] * e[2] + c[7] * e[5] + c[8] * e[8]
|
||||
])
|
||||
}
|
||||
|
||||
function ba(b, d, c, e) {
|
||||
a.swirl = a.swirl || u("uniform float radius;uniform float angle;uniform vec2 center;", "coord-=center;float distance=length(coord);if(distance<radius){float percent=(radius-distance)/radius;float theta=percent*percent*angle;float s=sin(theta);float c=cos(theta);coord=vec2(coord.x*c-coord.y*s,coord.x*s+coord.y*c);}coord+=center;");
|
||||
f.call(this, a.swirl, {
|
||||
radius: c,
|
||||
center: [b, d],
|
||||
angle: e,
|
||||
texSize: [this.width, this.height]
|
||||
});
|
||||
return this
|
||||
}
|
||||
var v = {};
|
||||
(function() {
|
||||
function a(b) {
|
||||
if (!b.getExtension("OES_texture_float")) return !1;
|
||||
var c = b.createFramebuffer(),
|
||||
e = b.createTexture();
|
||||
b.bindTexture(b.TEXTURE_2D, e);
|
||||
b.texParameteri(b.TEXTURE_2D, b.TEXTURE_MAG_FILTER, b.NEAREST);
|
||||
b.texParameteri(b.TEXTURE_2D, b.TEXTURE_MIN_FILTER, b.NEAREST);
|
||||
b.texParameteri(b.TEXTURE_2D, b.TEXTURE_WRAP_S, b.CLAMP_TO_EDGE);
|
||||
b.texParameteri(b.TEXTURE_2D, b.TEXTURE_WRAP_T, b.CLAMP_TO_EDGE);
|
||||
b.texImage2D(b.TEXTURE_2D, 0, b.RGBA, 1, 1, 0, b.RGBA, b.UNSIGNED_BYTE, null);
|
||||
b.bindFramebuffer(b.FRAMEBUFFER, c);
|
||||
b.framebufferTexture2D(b.FRAMEBUFFER, b.COLOR_ATTACHMENT0, b.TEXTURE_2D, e, 0);
|
||||
c = b.createTexture();
|
||||
b.bindTexture(b.TEXTURE_2D, c);
|
||||
b.texParameteri(b.TEXTURE_2D, b.TEXTURE_MAG_FILTER, b.LINEAR);
|
||||
b.texParameteri(b.TEXTURE_2D, b.TEXTURE_MIN_FILTER, b.LINEAR);
|
||||
b.texParameteri(b.TEXTURE_2D, b.TEXTURE_WRAP_S, b.CLAMP_TO_EDGE);
|
||||
b.texParameteri(b.TEXTURE_2D, b.TEXTURE_WRAP_T, b.CLAMP_TO_EDGE);
|
||||
b.texImage2D(b.TEXTURE_2D,
|
||||
0, b.RGBA, 2, 2, 0, b.RGBA, b.FLOAT, new Float32Array([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]));
|
||||
var e = b.createProgram(),
|
||||
d = b.createShader(b.VERTEX_SHADER),
|
||||
g = b.createShader(b.FRAGMENT_SHADER);
|
||||
b.shaderSource(d, "attribute vec2 vertex;void main(){gl_Position=vec4(vertex,0.0,1.0);}");
|
||||
b.shaderSource(g, "uniform sampler2D texture;void main(){gl_FragColor=texture2D(texture,vec2(0.5));}");
|
||||
b.compileShader(d);
|
||||
b.compileShader(g);
|
||||
b.attachShader(e, d);
|
||||
b.attachShader(e,
|
||||
g);
|
||||
b.linkProgram(e);
|
||||
d = b.createBuffer();
|
||||
b.bindBuffer(b.ARRAY_BUFFER, d);
|
||||
b.bufferData(b.ARRAY_BUFFER, new Float32Array([0, 0]), b.STREAM_DRAW);
|
||||
b.enableVertexAttribArray(0);
|
||||
b.vertexAttribPointer(0, 2, b.FLOAT, !1, 0, 0);
|
||||
d = new Uint8Array(4);
|
||||
b.useProgram(e);
|
||||
b.viewport(0, 0, 1, 1);
|
||||
b.bindTexture(b.TEXTURE_2D, c);
|
||||
b.drawArrays(b.POINTS, 0, 1);
|
||||
b.readPixels(0, 0, 1, 1, b.RGBA, b.UNSIGNED_BYTE, d);
|
||||
return 127 === d[0] || 128 === d[0]
|
||||
}
|
||||
|
||||
function d() {}
|
||||
|
||||
function c(a) {
|
||||
"OES_texture_float_linear" === a ? (void 0 === this.$OES_texture_float_linear$ && Object.defineProperty(this,
|
||||
"$OES_texture_float_linear$", {
|
||||
enumerable: !1,
|
||||
configurable: !1,
|
||||
writable: !1,
|
||||
value: new d
|
||||
}), a = this.$OES_texture_float_linear$) : a = n.call(this, a);
|
||||
return a
|
||||
}
|
||||
|
||||
function e() {
|
||||
var a = f.call(this); - 1 === a.indexOf("OES_texture_float_linear") && a.push("OES_texture_float_linear");
|
||||
return a
|
||||
}
|
||||
try {
|
||||
var g = document.createElement("canvas").getContext("experimental-webgl")
|
||||
} catch (l) {}
|
||||
if (g && -1 === g.getSupportedExtensions().indexOf("OES_texture_float_linear") && a(g)) {
|
||||
var n = WebGLRenderingContext.prototype.getExtension,
|
||||
f = WebGLRenderingContext.prototype.getSupportedExtensions;
|
||||
WebGLRenderingContext.prototype.getExtension = c;
|
||||
WebGLRenderingContext.prototype.getSupportedExtensions = e
|
||||
}
|
||||
})();
|
||||
var a;
|
||||
v.canvas = function() {
|
||||
var b = document.createElement("canvas");
|
||||
try {
|
||||
a = b.getContext("experimental-webgl", {
|
||||
premultipliedAlpha: !1
|
||||
})
|
||||
} catch (d) {
|
||||
a = null
|
||||
}
|
||||
if (!a) throw "This browser does not support WebGL";
|
||||
b._ = {
|
||||
gl: a,
|
||||
isInitialized: !1,
|
||||
texture: null,
|
||||
spareTexture: null,
|
||||
flippedShader: null
|
||||
};
|
||||
b.texture = k(A);
|
||||
b.draw = k(C);
|
||||
b.update = k(D);
|
||||
b.replace = k(E);
|
||||
b.contents = k(F);
|
||||
b.getPixelArray = k(G);
|
||||
b.brightnessContrast = k(H);
|
||||
b.hexagonalPixelate = k(X);
|
||||
b.hueSaturation = k(K);
|
||||
b.colorHalftone = k(U);
|
||||
b.triangleBlur = k(S);
|
||||
b.unsharpMask = k(N);
|
||||
b.perspective = k(aa);
|
||||
b.matrixWarp = k($);
|
||||
b.bulgePinch = k(Z);
|
||||
b.tiltShift = k(R);
|
||||
b.dotScreen = k(V);
|
||||
b.edgeWork = k(W);
|
||||
b.lensBlur = k(Q);
|
||||
b.zoomBlur = k(T);
|
||||
b.noise = k(L);
|
||||
b.denoise = k(J);
|
||||
b.curves = k(I);
|
||||
b.swirl = k(ba);
|
||||
b.ink = k(Y);
|
||||
b.vignette = k(P);
|
||||
b.vibrance = k(O);
|
||||
b.sepia = k(M);
|
||||
return b
|
||||
};
|
||||
v.splineInterpolate = t;
|
||||
var h = function() {
|
||||
function b(b, c) {
|
||||
var e = a.createShader(b);
|
||||
a.shaderSource(e, c);
|
||||
a.compileShader(e);
|
||||
if (!a.getShaderParameter(e,
|
||||
a.COMPILE_STATUS)) throw "compile error: " + a.getShaderInfoLog(e);
|
||||
return e
|
||||
}
|
||||
|
||||
function d(d, l) {
|
||||
this.texCoordAttribute = this.vertexAttribute = null;
|
||||
this.program = a.createProgram();
|
||||
d = d || c;
|
||||
l = l || e;
|
||||
l = "precision highp float;" + l;
|
||||
a.attachShader(this.program, b(a.VERTEX_SHADER, d));
|
||||
a.attachShader(this.program, b(a.FRAGMENT_SHADER, l));
|
||||
a.linkProgram(this.program);
|
||||
if (!a.getProgramParameter(this.program, a.LINK_STATUS)) throw "link error: " + a.getProgramInfoLog(this.program);
|
||||
}
|
||||
var c = "attribute vec2 vertex;attribute vec2 _texCoord;varying vec2 texCoord;void main(){texCoord=_texCoord;gl_Position=vec4(vertex*2.0-1.0,0.0,1.0);}",
|
||||
e = "uniform sampler2D texture;varying vec2 texCoord;void main(){gl_FragColor=texture2D(texture,texCoord);}";
|
||||
d.prototype.destroy = function() {
|
||||
a.deleteProgram(this.program);
|
||||
this.program = null
|
||||
};
|
||||
d.prototype.uniforms = function(b) {
|
||||
a.useProgram(this.program);
|
||||
for (var e in b)
|
||||
if (b.hasOwnProperty(e)) {
|
||||
var c = a.getUniformLocation(this.program, e);
|
||||
if (null !== c) {
|
||||
var d = b[e];
|
||||
if ("[object Array]" == Object.prototype.toString.call(d)) switch (d.length) {
|
||||
case 1:
|
||||
a.uniform1fv(c, new Float32Array(d));
|
||||
break;
|
||||
case 2:
|
||||
a.uniform2fv(c, new Float32Array(d));
|
||||
break;
|
||||
case 3:
|
||||
a.uniform3fv(c, new Float32Array(d));
|
||||
break;
|
||||
case 4:
|
||||
a.uniform4fv(c, new Float32Array(d));
|
||||
break;
|
||||
case 9:
|
||||
a.uniformMatrix3fv(c, !1, new Float32Array(d));
|
||||
break;
|
||||
case 16:
|
||||
a.uniformMatrix4fv(c, !1, new Float32Array(d));
|
||||
break;
|
||||
default:
|
||||
throw "dont't know how to load uniform \"" + e + '" of length ' + d.length;
|
||||
} else if ("[object Number]" == Object.prototype.toString.call(d)) a.uniform1f(c, d);
|
||||
else throw 'attempted to set uniform "' + e + '" to invalid value ' + (d || "undefined").toString();
|
||||
}
|
||||
}
|
||||
return this
|
||||
};
|
||||
d.prototype.textures = function(b) {
|
||||
a.useProgram(this.program);
|
||||
for (var c in b) b.hasOwnProperty(c) && a.uniform1i(a.getUniformLocation(this.program, c), b[c]);
|
||||
return this
|
||||
};
|
||||
d.prototype.drawRect = function(b, c, e, d) {
|
||||
var f = a.getParameter(a.VIEWPORT);
|
||||
c = void 0 !== c ? (c - f[1]) / f[3] : 0;
|
||||
b = void 0 !== b ? (b - f[0]) / f[2] : 0;
|
||||
e = void 0 !== e ? (e - f[0]) / f[2] : 1;
|
||||
d = void 0 !== d ? (d - f[1]) / f[3] : 1;
|
||||
null == a.vertexBuffer && (a.vertexBuffer = a.createBuffer());
|
||||
a.bindBuffer(a.ARRAY_BUFFER, a.vertexBuffer);
|
||||
a.bufferData(a.ARRAY_BUFFER, new Float32Array([b,
|
||||
c, b, d, e, c, e, d
|
||||
]), a.STATIC_DRAW);
|
||||
null == a.texCoordBuffer && (a.texCoordBuffer = a.createBuffer(), a.bindBuffer(a.ARRAY_BUFFER, a.texCoordBuffer), a.bufferData(a.ARRAY_BUFFER, new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]), a.STATIC_DRAW));
|
||||
null == this.vertexAttribute && (this.vertexAttribute = a.getAttribLocation(this.program, "vertex"), a.enableVertexAttribArray(this.vertexAttribute));
|
||||
null == this.texCoordAttribute && (this.texCoordAttribute = a.getAttribLocation(this.program, "_texCoord"), a.enableVertexAttribArray(this.texCoordAttribute));
|
||||
a.useProgram(this.program);
|
||||
a.bindBuffer(a.ARRAY_BUFFER, a.vertexBuffer);
|
||||
a.vertexAttribPointer(this.vertexAttribute, 2, a.FLOAT, !1, 0, 0);
|
||||
a.bindBuffer(a.ARRAY_BUFFER, a.texCoordBuffer);
|
||||
a.vertexAttribPointer(this.texCoordAttribute, 2, a.FLOAT, !1, 0, 0);
|
||||
a.drawArrays(a.TRIANGLE_STRIP, 0, 4)
|
||||
};
|
||||
d.getDefaultShader = function() {
|
||||
a.defaultShader = a.defaultShader || new d;
|
||||
return a.defaultShader
|
||||
};
|
||||
return d
|
||||
}();
|
||||
z.prototype.interpolate = function(a) {
|
||||
for (var d = 0, c = this.ya.length - 1; 1 < c - d;) {
|
||||
var e = c + d >> 1;
|
||||
this.xa[e] > a ? c = e : d = e
|
||||
}
|
||||
var e = this.xa[c] -
|
||||
this.xa[d],
|
||||
g = (this.xa[c] - a) / e;
|
||||
a = (a - this.xa[d]) / e;
|
||||
return g * this.ya[d] + a * this.ya[c] + ((g * g * g - g) * this.y2[d] + (a * a * a - a) * this.y2[c]) * e * e / 6
|
||||
};
|
||||
var r = function() {
|
||||
function b(b, c, d, f) {
|
||||
this.gl = a;
|
||||
this.id = a.createTexture();
|
||||
this.width = b;
|
||||
this.height = c;
|
||||
this.format = d;
|
||||
this.type = f;
|
||||
a.bindTexture(a.TEXTURE_2D, this.id);
|
||||
a.texParameteri(a.TEXTURE_2D, a.TEXTURE_MAG_FILTER, a.LINEAR);
|
||||
a.texParameteri(a.TEXTURE_2D, a.TEXTURE_MIN_FILTER, a.LINEAR);
|
||||
a.texParameteri(a.TEXTURE_2D, a.TEXTURE_WRAP_S, a.CLAMP_TO_EDGE);
|
||||
a.texParameteri(a.TEXTURE_2D,
|
||||
a.TEXTURE_WRAP_T, a.CLAMP_TO_EDGE);
|
||||
b && c && a.texImage2D(a.TEXTURE_2D, 0, this.format, b, c, 0, this.format, this.type, null)
|
||||
}
|
||||
|
||||
function d(a) {
|
||||
null == c && (c = document.createElement("canvas"));
|
||||
c.width = a.width;
|
||||
c.height = a.height;
|
||||
a = c.getContext("2d");
|
||||
a.clearRect(0, 0, c.width, c.height);
|
||||
return a
|
||||
}
|
||||
b.fromElement = function(c) {
|
||||
var d = new b(0, 0, a.RGBA, a.UNSIGNED_BYTE);
|
||||
d.loadContentsOf(c);
|
||||
return d
|
||||
};
|
||||
b.prototype.loadContentsOf = function(b) {
|
||||
this.width = b.width || b.videoWidth;
|
||||
this.height = b.height || b.videoHeight;
|
||||
a.bindTexture(a.TEXTURE_2D,
|
||||
this.id);
|
||||
a.texImage2D(a.TEXTURE_2D, 0, this.format, this.format, this.type, b)
|
||||
};
|
||||
b.prototype.initFromBytes = function(b, c, d) {
|
||||
this.width = b;
|
||||
this.height = c;
|
||||
this.format = a.RGBA;
|
||||
this.type = a.UNSIGNED_BYTE;
|
||||
a.bindTexture(a.TEXTURE_2D, this.id);
|
||||
a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, b, c, 0, a.RGBA, this.type, new Uint8Array(d))
|
||||
};
|
||||
b.prototype.initFromCanvas = function(b, c, d) {
|
||||
this.width = b;
|
||||
this.height = c;
|
||||
this.format = a.RGB;
|
||||
this.type = a.UNSIGNED_BYTE;
|
||||
a.bindTexture(a.TEXTURE_2D, this.id);
|
||||
// a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, b, c, 0, a.RGBA, this.type, d);
|
||||
a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, a.RGBA, a.UNSIGNED_BYTE, d);
|
||||
};
|
||||
b.prototype.destroy = function() {
|
||||
a.deleteTexture(this.id);
|
||||
this.id = null
|
||||
};
|
||||
b.prototype.use = function(b) {
|
||||
a.activeTexture(a.TEXTURE0 + (b || 0));
|
||||
a.bindTexture(a.TEXTURE_2D, this.id)
|
||||
};
|
||||
b.prototype.unuse = function(b) {
|
||||
a.activeTexture(a.TEXTURE0 +
|
||||
(b || 0));
|
||||
a.bindTexture(a.TEXTURE_2D, null)
|
||||
};
|
||||
b.prototype.ensureFormat = function(b, c, d, f) {
|
||||
if (1 == arguments.length) {
|
||||
var h = arguments[0];
|
||||
b = h.width;
|
||||
c = h.height;
|
||||
d = h.format;
|
||||
f = h.type
|
||||
}
|
||||
if (b != this.width || c != this.height || d != this.format || f != this.type) this.width = b, this.height = c, this.format = d, this.type = f, a.bindTexture(a.TEXTURE_2D, this.id), a.texImage2D(a.TEXTURE_2D, 0, this.format, b, c, 0, this.format, this.type, null)
|
||||
};
|
||||
b.prototype.drawTo = function(b) {
|
||||
a.framebuffer = a.framebuffer || a.createFramebuffer();
|
||||
a.bindFramebuffer(a.FRAMEBUFFER,
|
||||
a.framebuffer);
|
||||
a.framebufferTexture2D(a.FRAMEBUFFER, a.COLOR_ATTACHMENT0, a.TEXTURE_2D, this.id, 0);
|
||||
if (a.checkFramebufferStatus(a.FRAMEBUFFER) !== a.FRAMEBUFFER_COMPLETE) throw Error("incomplete framebuffer");
|
||||
a.viewport(0, 0, this.width, this.height);
|
||||
b();
|
||||
a.bindFramebuffer(a.FRAMEBUFFER, null)
|
||||
};
|
||||
var c = null;
|
||||
b.prototype.fillUsingCanvas = function(b) {
|
||||
b(d(this));
|
||||
this.format = a.RGBA;
|
||||
this.type = a.UNSIGNED_BYTE;
|
||||
a.bindTexture(a.TEXTURE_2D, this.id);
|
||||
a.texImage2D(a.TEXTURE_2D, 0, a.RGBA, a.RGBA, a.UNSIGNED_BYTE, c);
|
||||
return this
|
||||
};
|
||||
b.prototype.toImage = function(b) {
|
||||
this.use();
|
||||
h.getDefaultShader().drawRect();
|
||||
var f = 4 * this.width * this.height,
|
||||
k = new Uint8Array(f),
|
||||
n = d(this),
|
||||
p = n.createImageData(this.width, this.height);
|
||||
a.readPixels(0, 0, this.width, this.height, a.RGBA, a.UNSIGNED_BYTE, k);
|
||||
for (var m = 0; m < f; m++) p.data[m] = k[m];
|
||||
n.putImageData(p, 0, 0);
|
||||
b.src = c.toDataURL()
|
||||
};
|
||||
b.prototype.swapWith = function(a) {
|
||||
var b;
|
||||
b = a.id;
|
||||
a.id = this.id;
|
||||
this.id = b;
|
||||
b = a.width;
|
||||
a.width = this.width;
|
||||
this.width = b;
|
||||
b = a.height;
|
||||
a.height = this.height;
|
||||
this.height = b;
|
||||
b = a.format;
|
||||
a.format =
|
||||
this.format;
|
||||
this.format = b
|
||||
};
|
||||
return b
|
||||
}(),
|
||||
s = "float random(vec3 scale,float seed){return fract(sin(dot(gl_FragCoord.xyz+seed,scale))*43758.5453+seed);}";
|
||||
return v
|
||||
}();
|
1545
docs/vendor/particles.js
vendored
Normal file
1545
docs/vendor/particles.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
15
docs/wrappers/html.js
Normal file
15
docs/wrappers/html.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import React from 'react';
|
||||
import DocumentTitle from 'react-document-title';
|
||||
|
||||
class HTML extends React.Component {
|
||||
render() {
|
||||
var page = this.props.page.data;
|
||||
return (
|
||||
<div>
|
||||
<div dangerouslySetInnerHTML={{__html: page}}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HTML;
|
29
docs/wrappers/md.js
Normal file
29
docs/wrappers/md.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import React from 'react';
|
||||
import DocumentTitle from 'react-document-title';
|
||||
import { link } from 'gatsby-helpers';
|
||||
|
||||
var DOCS_BASEURL = "https://github.com/graphql-python/graphene/edit/master/docs/pages/";
|
||||
|
||||
class Markdown extends React.Component {
|
||||
render() {
|
||||
var post = this.props.page.data;
|
||||
var pagePath = this.props.page.requirePath;
|
||||
var documentUrl = `${DOCS_BASEURL}${pagePath}`;
|
||||
var showTitle = post.title && !this.props.docs;
|
||||
return (
|
||||
<DocumentTitle title={`${post.title?post.title+' - ':''}${this.props.config.siteTitle}`}>
|
||||
<div>
|
||||
{showTitle?<div className="page-title">
|
||||
<h1>{post.title}</h1>
|
||||
</div>:null}
|
||||
<div className="markdown">
|
||||
<div className={!this.props.docs?"wrapper":null} dangerouslySetInnerHTML={{__html: post.body}}/>
|
||||
<a href={documentUrl} className="improve-document-link">Edit page</a>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentTitle>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Markdown;
|
31
examples/complex_example.py
Normal file
31
examples/complex_example.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
import graphene
|
||||
|
||||
|
||||
class GeoInput(graphene.InputObjectType):
|
||||
lat = graphene.Float(required=True)
|
||||
lng = graphene.Float(required=True)
|
||||
|
||||
|
||||
class Address(graphene.ObjectType):
|
||||
latlng = graphene.String()
|
||||
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
address = graphene.Field(Address, geo=graphene.Argument(GeoInput))
|
||||
|
||||
def resolve_address(self, args, info):
|
||||
geo = args.get('geo')
|
||||
return Address(latlng="({},{})".format(geo.get('lat'), geo.get('lng')))
|
||||
|
||||
|
||||
schema = graphene.Schema(query=Query)
|
||||
query = '''
|
||||
query something{
|
||||
address(geo: {lat:32.2, lng:12}) {
|
||||
latlng
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
result = schema.execute(query)
|
||||
print(result.data['address']['latlng'])
|
64
examples/cookbook_django/README.md
Normal file
64
examples/cookbook_django/README.md
Normal file
|
@ -0,0 +1,64 @@
|
|||
Cookbook Example Django Project
|
||||
===============================
|
||||
|
||||
This example project demos integration between Graphene and Django.
|
||||
The project contains two apps, one named `ingredients` and another
|
||||
named `recepies`.
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
|
||||
First you'll need to get the source of the project. Do this by cloning the
|
||||
whole Graphene repository:
|
||||
|
||||
```bash
|
||||
# Get the example project code
|
||||
git clone https://github.com/graphql-python/graphene.git
|
||||
cd graphene/examples/cookbook
|
||||
```
|
||||
|
||||
It is good idea (but not required) to create a virtual environment
|
||||
for this project. We'll do this using
|
||||
[virtualenv](http://docs.python-guide.org/en/latest/dev/virtualenvs/)
|
||||
to keep things simple,
|
||||
but you may also find something like
|
||||
[virtualenvwrapper](https://virtualenvwrapper.readthedocs.org/en/latest/)
|
||||
to be useful:
|
||||
|
||||
```bash
|
||||
# Create a virtualenv in which we can install the dependencies
|
||||
virtualenv env
|
||||
source env/bin/activate
|
||||
```
|
||||
|
||||
Now we can install our dependencies:
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
Now setup our database:
|
||||
|
||||
```bash
|
||||
# Setup the database
|
||||
./manage.py migrate
|
||||
|
||||
# Load some example data
|
||||
./manage.py loaddata ingredients
|
||||
|
||||
# Create an admin user (useful for logging into the admin UI
|
||||
# at http://127.0.0.1:8000/admin)
|
||||
./manage.py createsuperuser
|
||||
```
|
||||
|
||||
Now you should be ready to start the server:
|
||||
|
||||
```bash
|
||||
./manage.py runserver
|
||||
```
|
||||
|
||||
Now head on over to
|
||||
[http://127.0.0.1:8000/graphiql](http://127.0.0.1:8000/graphiql)
|
||||
and run some queries!
|
||||
(See the [Django quickstart guide](http://graphene-python.org/docs/quickstart-django/)
|
||||
for some example queries)
|
0
examples/cookbook_django/cookbook/__init__.py
Normal file
0
examples/cookbook_django/cookbook/__init__.py
Normal file
6
examples/cookbook_django/cookbook/ingredients/admin.py
Normal file
6
examples/cookbook_django/cookbook/ingredients/admin.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from cookbook.ingredients.models import Category, Ingredient
|
||||
|
||||
admin.site.register(Ingredient)
|
||||
admin.site.register(Category)
|
7
examples/cookbook_django/cookbook/ingredients/apps.py
Normal file
7
examples/cookbook_django/cookbook/ingredients/apps.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class IngredientsConfig(AppConfig):
|
||||
name = 'cookbook.ingredients'
|
||||
label = 'ingredients'
|
||||
verbose_name = 'Ingredients'
|
|
@ -0,0 +1 @@
|
|||
[{"model": "ingredients.category", "pk": 1, "fields": {"name": "Dairy"}}, {"model": "ingredients.category", "pk": 2, "fields": {"name": "Meat"}}, {"model": "ingredients.ingredient", "pk": 1, "fields": {"name": "Eggs", "notes": "Good old eggs", "category": 1}}, {"model": "ingredients.ingredient", "pk": 2, "fields": {"name": "Milk", "notes": "Comes from a cow", "category": 1}}, {"model": "ingredients.ingredient", "pk": 3, "fields": {"name": "Beef", "notes": "Much like milk, this comes from a cow", "category": 2}}, {"model": "ingredients.ingredient", "pk": 4, "fields": {"name": "Chicken", "notes": "Definitely doesn't come from a cow", "category": 2}}]
|
|
@ -0,0 +1,33 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9 on 2015-12-04 18:15
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Category',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Ingredient',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('notes', models.TextField()),
|
||||
('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ingredients', to='ingredients.Category')),
|
||||
],
|
||||
),
|
||||
]
|
17
examples/cookbook_django/cookbook/ingredients/models.py
Normal file
17
examples/cookbook_django/cookbook/ingredients/models.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from django.db import models
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Ingredient(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
notes = models.TextField()
|
||||
category = models.ForeignKey(Category, related_name='ingredients')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
39
examples/cookbook_django/cookbook/ingredients/schema.py
Normal file
39
examples/cookbook_django/cookbook/ingredients/schema.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
from cookbook.ingredients.models import Category, Ingredient
|
||||
from graphene import ObjectType, relay
|
||||
from graphene.contrib.django.filter import DjangoFilterConnectionField
|
||||
from graphene.contrib.django.types import DjangoNode
|
||||
|
||||
|
||||
# Graphene will automatically map the User model's fields onto the UserType.
|
||||
# This is configured in the UserType's Meta class (as you can see below)
|
||||
class CategoryNode(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = Category
|
||||
filter_fields = ['name', 'ingredients']
|
||||
filter_order_by = ['name']
|
||||
|
||||
|
||||
class IngredientNode(DjangoNode):
|
||||
|
||||
class Meta:
|
||||
model = Ingredient
|
||||
# Allow for some more advanced filtering here
|
||||
filter_fields = {
|
||||
'name': ['exact', 'icontains', 'istartswith'],
|
||||
'notes': ['exact', 'icontains'],
|
||||
'category': ['exact'],
|
||||
'category__name': ['exact'],
|
||||
}
|
||||
filter_order_by = ['name', 'category__name']
|
||||
|
||||
|
||||
class Query(ObjectType):
|
||||
category = relay.NodeField(CategoryNode)
|
||||
all_categories = DjangoFilterConnectionField(CategoryNode)
|
||||
|
||||
ingredient = relay.NodeField(IngredientNode)
|
||||
all_ingredients = DjangoFilterConnectionField(IngredientNode)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
3
examples/cookbook_django/cookbook/ingredients/tests.py
Normal file
3
examples/cookbook_django/cookbook/ingredients/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
3
examples/cookbook_django/cookbook/ingredients/views.py
Normal file
3
examples/cookbook_django/cookbook/ingredients/views.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
6
examples/cookbook_django/cookbook/recipes/admin.py
Normal file
6
examples/cookbook_django/cookbook/recipes/admin.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from cookbook.recipes.models import Recipe, RecipeIngredient
|
||||
|
||||
admin.site.register(Recipe)
|
||||
admin.site.register(RecipeIngredient)
|
7
examples/cookbook_django/cookbook/recipes/apps.py
Normal file
7
examples/cookbook_django/cookbook/recipes/apps.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class RecipesConfig(AppConfig):
|
||||
name = 'cookbook.recipes'
|
||||
label = 'recipes'
|
||||
verbose_name = 'Recipes'
|
|
@ -0,0 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9 on 2015-12-04 18:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('ingredients', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Recipe',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=100)),
|
||||
('instructions', models.TextField()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RecipeIngredient',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('amount', models.FloatField()),
|
||||
('unit', models.CharField(choices=[('kg', 'Kilograms'), ('l', 'Litres'), ('', 'Units')], max_length=20)),
|
||||
('ingredient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='used_by', to='ingredients.Ingredient')),
|
||||
('recipes', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='amounts', to='recipes.Recipe')),
|
||||
],
|
||||
),
|
||||
]
|
19
examples/cookbook_django/cookbook/recipes/models.py
Normal file
19
examples/cookbook_django/cookbook/recipes/models.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
from django.db import models
|
||||
|
||||
from cookbook.ingredients.models import Ingredient
|
||||
|
||||
|
||||
class Recipe(models.Model):
|
||||
title = models.CharField(max_length=100)
|
||||
instructions = models.TextField()
|
||||
|
||||
|
||||
class RecipeIngredient(models.Model):
|
||||
recipes = models.ForeignKey(Recipe, related_name='amounts')
|
||||
ingredient = models.ForeignKey(Ingredient, related_name='used_by')
|
||||
amount = models.FloatField()
|
||||
unit = models.CharField(max_length=20, choices=(
|
||||
('kg', 'Kilograms'),
|
||||
('l', 'Litres'),
|
||||
('', 'Units'),
|
||||
))
|
3
examples/cookbook_django/cookbook/recipes/tests.py
Normal file
3
examples/cookbook_django/cookbook/recipes/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
3
examples/cookbook_django/cookbook/recipes/views.py
Normal file
3
examples/cookbook_django/cookbook/recipes/views.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
9
examples/cookbook_django/cookbook/schema.py
Normal file
9
examples/cookbook_django/cookbook/schema.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
import cookbook.ingredients.schema
|
||||
import graphene
|
||||
|
||||
|
||||
class Query(cookbook.ingredients.schema.Query):
|
||||
pass
|
||||
|
||||
schema = graphene.Schema(name='Cookbook Schema')
|
||||
schema.query = Query
|
125
examples/cookbook_django/cookbook/settings.py
Normal file
125
examples/cookbook_django/cookbook/settings.py
Normal file
|
@ -0,0 +1,125 @@
|
|||
"""
|
||||
Django settings for cookbook project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 1.9.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.9/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.9/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '_$=$%eqxk$8ss4n7mtgarw^5$8^d5+c83!vwatr@i_81myb=e4'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_graphiql',
|
||||
|
||||
'cookbook.ingredients.apps.IngredientsConfig',
|
||||
'cookbook.recipes.apps.RecipesConfig',
|
||||
]
|
||||
|
||||
MIDDLEWARE_CLASSES = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'cookbook.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'cookbook.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.9/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.9/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
12
examples/cookbook_django/cookbook/urls.py
Normal file
12
examples/cookbook_django/cookbook/urls.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from cookbook.schema import schema
|
||||
from graphene.contrib.django.views import GraphQLView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^graphql', csrf_exempt(GraphQLView.as_view(schema=schema))),
|
||||
url(r'^graphiql', include('django_graphiql.urls')),
|
||||
]
|
16
examples/cookbook_django/cookbook/wsgi.py
Normal file
16
examples/cookbook_django/cookbook/wsgi.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
WSGI config for cookbook project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cookbook.settings")
|
||||
|
||||
application = get_wsgi_application()
|
10
examples/cookbook_django/manage.py
Executable file
10
examples/cookbook_django/manage.py
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cookbook.settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
5
examples/cookbook_django/requirements.txt
Normal file
5
examples/cookbook_django/requirements.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
graphene[django]
|
||||
django_graphiql
|
||||
graphql-core
|
||||
django==1.9
|
||||
django-filter==0.11.0
|
|
@ -21,7 +21,7 @@ query = '''
|
|||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
result = schema.execute(query)
|
||||
print(result.data['patron'])
|
|
@ -63,15 +63,15 @@ class Query(graphene.ObjectType):
|
|||
|
||||
@resolve_only_args
|
||||
def resolve_ships(self):
|
||||
return [Ship(s) for s in get_ships()]
|
||||
return get_ships()
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_rebels(self):
|
||||
return Faction(get_rebels())
|
||||
return get_rebels()
|
||||
|
||||
@resolve_only_args
|
||||
def resolve_empire(self):
|
||||
return Faction(get_empire())
|
||||
return get_empire()
|
||||
|
||||
|
||||
class Mutation(graphene.ObjectType):
|
||||
|
|
|
@ -4,16 +4,14 @@ from graphql.core.type import (
|
|||
|
||||
from graphene import signals
|
||||
|
||||
from graphene.core.schema import (
|
||||
Schema
|
||||
)
|
||||
|
||||
from graphene.core.types import (
|
||||
from .core import (
|
||||
Schema,
|
||||
ObjectType,
|
||||
InputObjectType,
|
||||
Interface,
|
||||
Mutation,
|
||||
BaseType,
|
||||
Scalar,
|
||||
InstanceType,
|
||||
LazyType,
|
||||
Argument,
|
||||
Field,
|
||||
|
@ -53,12 +51,13 @@ __all__ = [
|
|||
'NonNull',
|
||||
'signals',
|
||||
'Schema',
|
||||
'BaseType',
|
||||
'InstanceType',
|
||||
'LazyType',
|
||||
'ObjectType',
|
||||
'InputObjectType',
|
||||
'Interface',
|
||||
'Mutation',
|
||||
'Scalar',
|
||||
'Field',
|
||||
'InputField',
|
||||
'StringField',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from graphene.contrib.django.types import (
|
||||
DjangoConnection,
|
||||
DjangoObjectType,
|
||||
DjangoInterface,
|
||||
DjangoNode
|
||||
)
|
||||
from graphene.contrib.django.fields import (
|
||||
|
@ -8,5 +8,5 @@ from graphene.contrib.django.fields import (
|
|||
DjangoModelField
|
||||
)
|
||||
|
||||
__all__ = ['DjangoObjectType', 'DjangoInterface', 'DjangoNode',
|
||||
'DjangoConnectionField', 'DjangoModelField']
|
||||
__all__ = ['DjangoObjectType', 'DjangoNode', 'DjangoConnection',
|
||||
'DjangoModelField', 'DjangoConnectionField']
|
||||
|
|
15
graphene/contrib/django/compat.py
Normal file
15
graphene/contrib/django/compat.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from django.db import models
|
||||
|
||||
try:
|
||||
UUIDField = models.UUIDField
|
||||
except AttributeError:
|
||||
# Improved compatibility for Django 1.6
|
||||
class UUIDField(object):
|
||||
pass
|
||||
|
||||
try:
|
||||
from django.db.models.related import RelatedObject
|
||||
except:
|
||||
# Improved compatibility for Django 1.6
|
||||
class RelatedObject(object):
|
||||
pass
|
|
@ -1,15 +1,10 @@
|
|||
from django.db import models
|
||||
from singledispatch import singledispatch
|
||||
|
||||
from ...core.types.scalars import ID, Boolean, Float, Int, String
|
||||
from .fields import ConnectionOrListField, DjangoModelField
|
||||
from .compat import RelatedObject, UUIDField
|
||||
from .utils import get_related_model, import_single_dispatch
|
||||
|
||||
try:
|
||||
UUIDField = models.UUIDField
|
||||
except AttributeError:
|
||||
# Improved compatibility for Django 1.6
|
||||
class UUIDField(object):
|
||||
pass
|
||||
singledispatch = import_single_dispatch()
|
||||
|
||||
|
||||
@singledispatch
|
||||
|
@ -25,6 +20,7 @@ def convert_django_field(field):
|
|||
@convert_django_field.register(models.EmailField)
|
||||
@convert_django_field.register(models.SlugField)
|
||||
@convert_django_field.register(models.URLField)
|
||||
@convert_django_field.register(models.GenericIPAddressField)
|
||||
@convert_django_field.register(UUIDField)
|
||||
def convert_field_to_string(field):
|
||||
return String(description=field.help_text)
|
||||
|
@ -62,12 +58,23 @@ def convert_field_to_float(field):
|
|||
|
||||
@convert_django_field.register(models.ManyToManyField)
|
||||
@convert_django_field.register(models.ManyToOneRel)
|
||||
@convert_django_field.register(models.ManyToManyRel)
|
||||
def convert_field_to_list_or_connection(field):
|
||||
model_field = DjangoModelField(field.related_model)
|
||||
from .fields import DjangoModelField, ConnectionOrListField
|
||||
model_field = DjangoModelField(get_related_model(field))
|
||||
return ConnectionOrListField(model_field)
|
||||
|
||||
|
||||
# For Django 1.6
|
||||
@convert_django_field.register(RelatedObject)
|
||||
def convert_relatedfield_to_djangomodel(field):
|
||||
from .fields import DjangoModelField, ConnectionOrListField
|
||||
model_field = DjangoModelField(field.model)
|
||||
return ConnectionOrListField(model_field)
|
||||
|
||||
|
||||
@convert_django_field.register(models.OneToOneField)
|
||||
@convert_django_field.register(models.ForeignKey)
|
||||
def convert_field_to_djangomodel(field):
|
||||
return DjangoModelField(field.related_model, description=field.help_text)
|
||||
from .fields import DjangoModelField
|
||||
return DjangoModelField(get_related_model(field), description=field.help_text)
|
||||
|
|
4
graphene/contrib/django/debug/__init__.py
Normal file
4
graphene/contrib/django/debug/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
from .plugin import DjangoDebugPlugin
|
||||
from .types import DjangoDebug
|
||||
|
||||
__all__ = ['DjangoDebugPlugin', 'DjangoDebug']
|
79
graphene/contrib/django/debug/plugin.py
Normal file
79
graphene/contrib/django/debug/plugin.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
from contextlib import contextmanager
|
||||
|
||||
from django.db import connections
|
||||
|
||||
from ....core.schema import GraphQLSchema
|
||||
from ....core.types import Field
|
||||
from ....plugins import Plugin
|
||||
from .sql.tracking import unwrap_cursor, wrap_cursor
|
||||
from .sql.types import DjangoDebugSQL
|
||||
from .types import DjangoDebug
|
||||
|
||||
|
||||
class WrappedRoot(object):
|
||||
|
||||
def __init__(self, root):
|
||||
self._recorded = []
|
||||
self._root = root
|
||||
|
||||
def record(self, **log):
|
||||
self._recorded.append(DjangoDebugSQL(**log))
|
||||
|
||||
def debug(self):
|
||||
return DjangoDebug(sql=self._recorded)
|
||||
|
||||
|
||||
class WrapRoot(object):
|
||||
|
||||
@property
|
||||
def _root(self):
|
||||
return self._wrapped_root.root
|
||||
|
||||
@_root.setter
|
||||
def _root(self, value):
|
||||
self._wrapped_root = value
|
||||
|
||||
def resolve_debug(self, args, info):
|
||||
return self._wrapped_root.debug()
|
||||
|
||||
|
||||
def debug_objecttype(objecttype):
|
||||
return type(
|
||||
'Debug{}'.format(objecttype._meta.type_name),
|
||||
(WrapRoot, objecttype),
|
||||
{'debug': Field(DjangoDebug, name='__debug')})
|
||||
|
||||
|
||||
class DjangoDebugPlugin(Plugin):
|
||||
|
||||
def enable_instrumentation(self, wrapped_root):
|
||||
# This is thread-safe because database connections are thread-local.
|
||||
for connection in connections.all():
|
||||
wrap_cursor(connection, wrapped_root)
|
||||
|
||||
def disable_instrumentation(self):
|
||||
for connection in connections.all():
|
||||
unwrap_cursor(connection)
|
||||
|
||||
def wrap_schema(self, schema_type):
|
||||
query = schema_type._query
|
||||
if query:
|
||||
class_type = self.schema.objecttype(schema_type.get_query_type())
|
||||
assert class_type, 'The query in schema is not constructed with graphene'
|
||||
_type = debug_objecttype(class_type)
|
||||
self.schema.register(_type, force=True)
|
||||
return GraphQLSchema(
|
||||
self.schema,
|
||||
self.schema.T(_type),
|
||||
schema_type.get_mutation_type(),
|
||||
schema_type.get_subscription_type()
|
||||
)
|
||||
return schema_type
|
||||
|
||||
@contextmanager
|
||||
def context_execution(self, executor):
|
||||
executor['root'] = WrappedRoot(root=executor['root'])
|
||||
executor['schema'] = self.wrap_schema(executor['schema'])
|
||||
self.enable_instrumentation(executor['root'])
|
||||
yield executor
|
||||
self.disable_instrumentation()
|
0
graphene/contrib/django/debug/sql/__init__.py
Normal file
0
graphene/contrib/django/debug/sql/__init__.py
Normal file
165
graphene/contrib/django/debug/sql/tracking.py
Normal file
165
graphene/contrib/django/debug/sql/tracking.py
Normal file
|
@ -0,0 +1,165 @@
|
|||
# Code obtained from django-debug-toolbar sql panel tracking
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
import json
|
||||
from threading import local
|
||||
from time import time
|
||||
|
||||
from django.utils import six
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
|
||||
class SQLQueryTriggered(Exception):
|
||||
"""Thrown when template panel triggers a query"""
|
||||
|
||||
|
||||
class ThreadLocalState(local):
|
||||
|
||||
def __init__(self):
|
||||
self.enabled = True
|
||||
|
||||
@property
|
||||
def Wrapper(self):
|
||||
if self.enabled:
|
||||
return NormalCursorWrapper
|
||||
return ExceptionCursorWrapper
|
||||
|
||||
def recording(self, v):
|
||||
self.enabled = v
|
||||
|
||||
|
||||
state = ThreadLocalState()
|
||||
recording = state.recording # export function
|
||||
|
||||
|
||||
def wrap_cursor(connection, panel):
|
||||
if not hasattr(connection, '_djdt_cursor'):
|
||||
connection._djdt_cursor = connection.cursor
|
||||
|
||||
def cursor():
|
||||
return state.Wrapper(connection._djdt_cursor(), connection, panel)
|
||||
|
||||
connection.cursor = cursor
|
||||
return cursor
|
||||
|
||||
|
||||
def unwrap_cursor(connection):
|
||||
if hasattr(connection, '_djdt_cursor'):
|
||||
del connection._djdt_cursor
|
||||
del connection.cursor
|
||||
|
||||
|
||||
class ExceptionCursorWrapper(object):
|
||||
"""
|
||||
Wraps a cursor and raises an exception on any operation.
|
||||
Used in Templates panel.
|
||||
"""
|
||||
|
||||
def __init__(self, cursor, db, logger):
|
||||
pass
|
||||
|
||||
def __getattr__(self, attr):
|
||||
raise SQLQueryTriggered()
|
||||
|
||||
|
||||
class NormalCursorWrapper(object):
|
||||
"""
|
||||
Wraps a cursor and logs queries.
|
||||
"""
|
||||
|
||||
def __init__(self, cursor, db, logger):
|
||||
self.cursor = cursor
|
||||
# Instance of a BaseDatabaseWrapper subclass
|
||||
self.db = db
|
||||
# logger must implement a ``record`` method
|
||||
self.logger = logger
|
||||
|
||||
def _quote_expr(self, element):
|
||||
if isinstance(element, six.string_types):
|
||||
return "'%s'" % force_text(element).replace("'", "''")
|
||||
else:
|
||||
return repr(element)
|
||||
|
||||
def _quote_params(self, params):
|
||||
if not params:
|
||||
return params
|
||||
if isinstance(params, dict):
|
||||
return dict((key, self._quote_expr(value))
|
||||
for key, value in params.items())
|
||||
return list(map(self._quote_expr, params))
|
||||
|
||||
def _decode(self, param):
|
||||
try:
|
||||
return force_text(param, strings_only=True)
|
||||
except UnicodeDecodeError:
|
||||
return '(encoded string)'
|
||||
|
||||
def _record(self, method, sql, params):
|
||||
start_time = time()
|
||||
try:
|
||||
return method(sql, params)
|
||||
finally:
|
||||
stop_time = time()
|
||||
duration = (stop_time - start_time)
|
||||
_params = ''
|
||||
try:
|
||||
_params = json.dumps(list(map(self._decode, params)))
|
||||
except Exception:
|
||||
pass # object not JSON serializable
|
||||
|
||||
alias = getattr(self.db, 'alias', 'default')
|
||||
conn = self.db.connection
|
||||
vendor = getattr(conn, 'vendor', 'unknown')
|
||||
|
||||
params = {
|
||||
'vendor': vendor,
|
||||
'alias': alias,
|
||||
'sql': self.db.ops.last_executed_query(
|
||||
self.cursor, sql, self._quote_params(params)),
|
||||
'duration': duration,
|
||||
'raw_sql': sql,
|
||||
'params': _params,
|
||||
'start_time': start_time,
|
||||
'stop_time': stop_time,
|
||||
'is_slow': duration > 10,
|
||||
'is_select': sql.lower().strip().startswith('select'),
|
||||
}
|
||||
|
||||
if vendor == 'postgresql':
|
||||
# If an erroneous query was ran on the connection, it might
|
||||
# be in a state where checking isolation_level raises an
|
||||
# exception.
|
||||
try:
|
||||
iso_level = conn.isolation_level
|
||||
except conn.InternalError:
|
||||
iso_level = 'unknown'
|
||||
params.update({
|
||||
'trans_id': self.logger.get_transaction_id(alias),
|
||||
'trans_status': conn.get_transaction_status(),
|
||||
'iso_level': iso_level,
|
||||
'encoding': conn.encoding,
|
||||
})
|
||||
|
||||
# We keep `sql` to maintain backwards compatibility
|
||||
self.logger.record(**params)
|
||||
|
||||
def callproc(self, procname, params=()):
|
||||
return self._record(self.cursor.callproc, procname, params)
|
||||
|
||||
def execute(self, sql, params=()):
|
||||
return self._record(self.cursor.execute, sql, params)
|
||||
|
||||
def executemany(self, sql, param_list):
|
||||
return self._record(self.cursor.executemany, sql, param_list)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.cursor, attr)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.cursor)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.close()
|
19
graphene/contrib/django/debug/sql/types.py
Normal file
19
graphene/contrib/django/debug/sql/types.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
from .....core import Boolean, Float, ObjectType, String
|
||||
|
||||
|
||||
class DjangoDebugSQL(ObjectType):
|
||||
vendor = String()
|
||||
alias = String()
|
||||
sql = String()
|
||||
duration = Float()
|
||||
raw_sql = String()
|
||||
params = String()
|
||||
start_time = Float()
|
||||
stop_time = Float()
|
||||
is_slow = Boolean()
|
||||
is_select = Boolean()
|
||||
|
||||
trans_id = String()
|
||||
trans_status = String()
|
||||
iso_level = String()
|
||||
encoding = String()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user