Skip to content

Commit c84d279

Browse files
committed
feat: Added discovery of Python peer from executors dir
1 parent 3fdb65a commit c84d279

2 files changed

Lines changed: 102 additions & 51 deletions

File tree

src/serve.ts

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,38 @@ replaceHandlers(data =>
2121
})
2222
)
2323

24-
// Discover other executors registered on this machine
25-
// In the future this may attempt to discover remote executors as well
26-
const manifests = discoverStdio()
27-
if (manifests.length === 0) {
28-
log.warn(
29-
'No peer executors discovered on this machine. Executor will have limited capabilities.'
30-
)
31-
}
24+
async function main() {
25+
// Discover other executors registered on this machine
26+
// In the future this may attempt to discover remote executors as well
27+
const manifests = await discoverStdio()
28+
if (manifests.length === 0) {
29+
log.warn(
30+
'No peer executors discovered on this machine. Executor will have limited capabilities.'
31+
)
32+
}
3233

33-
// Create a list of client types that can be used by executor
34-
const clientTypes: ClientType[] = [StdioClient as ClientType]
34+
// Create a list of client types that can be used by executor
35+
const clientTypes: ClientType[] = [StdioClient as ClientType]
3536

36-
// Create executor that will served by servers
37-
const executor = new Executor(manifests, clientTypes)
37+
// Create executor that will served by servers
38+
const executor = new Executor(manifests, clientTypes)
3839

39-
// Add server classes based on supplied options
40-
const servers: Server[] = []
41-
if (options.tcp !== undefined) {
42-
servers.push(new TcpServer(executor, options.tcp))
43-
}
44-
if (servers.length === 0) {
45-
log.warn(
46-
'No servers specified in options (e.g. --tcp --stdio). Executor will not be accessible.'
47-
)
40+
// Add server classes based on supplied options
41+
const servers: Server[] = []
42+
if (options.tcp !== undefined) {
43+
servers.push(new TcpServer(executor, options.tcp))
44+
}
45+
if (servers.length === 0) {
46+
log.warn(
47+
'No servers specified in options (e.g. --tcp --stdio). Executor will not be accessible.'
48+
)
49+
}
50+
51+
executor.start(servers)
4852
}
4953

50-
executor.start(servers)
54+
main()
55+
.then(() => {})
56+
.catch(err => {
57+
log.error(err)
58+
})

src/stdio/discover.ts

Lines changed: 72 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,89 @@
11
import { Manifest } from '../base/Executor'
22
import { Transport } from '../base/Transports'
3+
import * as path from 'path'
4+
import * as os from 'os'
5+
import * as fs from 'fs'
6+
import * as util from 'util'
7+
import { getLogger } from '@stencila/logga'
38

4-
export default function discover(): Manifest[] {
9+
const glob = util.promisify(require('glob'))
10+
11+
const log = getLogger('executa:serve')
12+
13+
const EXECUTORS_DIR_NAME = 'executors'
14+
15+
export default async function discover(): Promise<Manifest[]> {
516
// TODO: implement discovery of manifest files from ~/.stencila/executors/ (or similar)
617
// Should be able to mostly copy and paste from
718
// https://github.com/stencila/node/blob/24c30d1c89b5f6b6719a0beeda7a55d19401c294/lib/host/Host.js#L654-L666
819
// See https://github.com/stencila/executa/issues/2
9-
return [python, js]
10-
}
1120

12-
// These are just stubs to be replaced by JSON read in from manifest.json files...
21+
let stencilaHome: string
1322

14-
const python: Manifest = {
15-
capabilities: {
16-
execute: {
17-
type: 'object',
18-
required: ['node'],
19-
properties: {
20-
node: {
21-
type: 'object',
22-
required: ['type', 'programmingLanguage'],
23-
properties: {
24-
type: {
25-
enum: ['CodeChunk', 'CodeExpression']
26-
},
27-
programmingLanguage: {
28-
enum: ['python']
29-
}
30-
}
31-
}
32-
}
23+
switch (os.platform()) {
24+
case 'darwin':
25+
stencilaHome = path.join(
26+
process.env.HOME !== undefined ? process.env.HOME : '',
27+
'Library',
28+
'Application Support',
29+
'Stencila'
30+
)
31+
break
32+
case 'linux':
33+
stencilaHome = path.join(
34+
process.env.HOME !== undefined ? process.env.HOME : '',
35+
'.stencila'
36+
)
37+
break
38+
case 'win32': // is 'win32' even on 64 bit windows systems
39+
stencilaHome = path.join(
40+
process.env.APPDATA !== undefined ? process.env.APPDATA : '',
41+
'Stencila'
42+
)
43+
break
44+
default:
45+
stencilaHome = path.join(
46+
process.env.HOME !== undefined ? process.env.HOME : '',
47+
'stencila'
48+
)
49+
}
50+
const executorsDir = path.join(stencilaHome, EXECUTORS_DIR_NAME)
51+
52+
const manifests: Manifest[] = []
53+
54+
const discoverDir = async (dir: string): Promise<Manifest | undefined> => {
55+
// Check the folder exists (they may not e.g. if no packages have been registered)
56+
try {
57+
fs.accessSync(dir, fs.constants.R_OK)
58+
} catch (error) {
59+
return
3360
}
34-
},
35-
addresses: {
36-
stdio: {
37-
type: Transport.stdio,
38-
command: 'python3',
39-
args: ['-m', 'stencila.schema', 'listen']
61+
// For each host in the directory
62+
for (const file of await glob(path.join(dir, '*.json'))) {
63+
let json
64+
try {
65+
json = fs.readFileSync(file, { encoding: 'utf8' })
66+
} catch (error) {
67+
log.warn(`Warning: error reading file "${file}": ${error.message}`)
68+
continue
69+
}
70+
71+
try {
72+
manifests.push(JSON.parse(json) as Manifest)
73+
} catch (error) {
74+
log.warn(`Warning: error parsing file "${file}": ${error.message}`)
75+
}
4076
}
4177
}
78+
79+
await discoverDir(executorsDir)
80+
81+
manifests.push(js)
82+
return manifests
4283
}
4384

85+
// These are just stubs to be replaced by JSON read in from manifest.json files...
86+
4487
const js: Manifest = {
4588
capabilities: {
4689
execute: {

0 commit comments

Comments
 (0)